From 0f7a384b39e91ce801806658ee4e0d06862e5824 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Mon, 6 Jul 2020 00:35:52 +0300 Subject: [PATCH] Added references.zig, use it from rename.zig --- src/main.zig | 2 +- src/references.zig | 410 ++++++++++++++++++++++++++++++++++++++++++++ src/rename.zig | 415 ++++----------------------------------------- src/requests.zig | 8 + 4 files changed, 455 insertions(+), 380 deletions(-) create mode 100644 src/references.zig diff --git a/src/main.zig b/src/main.zig index 6dad69b..d31aae6 100644 --- a/src/main.zig +++ b/src/main.zig @@ -89,7 +89,7 @@ var client_capabilities = ClientCapabilities{}; var offset_encoding = offsets.Encoding.utf16; const initialize_capabilities = - \\"capabilities": {"signatureHelpProvider": {"triggerCharacters": ["(",","]},"textDocumentSync": 1,"renameProvider":true,"completionProvider": {"resolveProvider": false,"triggerCharacters": [".",":","@"]},"documentHighlightProvider": false,"hoverProvider": true,"codeActionProvider": false,"declarationProvider": true,"definitionProvider": true,"typeDefinitionProvider": true,"implementationProvider": false,"referencesProvider": false,"documentSymbolProvider": true,"colorProvider": false,"documentFormattingProvider": true,"documentRangeFormattingProvider": false,"foldingRangeProvider": false,"selectionRangeProvider": false,"workspaceSymbolProvider": false,"rangeProvider": false,"documentProvider": true,"workspace": {"workspaceFolders": {"supported": true,"changeNotifications": true}},"semanticTokensProvider": {"documentProvider": true,"legend": {"tokenTypes": ["namespace","type","struct","enum","union","parameter","variable","tagField","field","errorTag","function","keyword","comment","string","number","operator","builtin","label"],"tokenModifiers": ["definition","async","documentation", "generic"]}}}}} + \\"capabilities": {"signatureHelpProvider": {"triggerCharacters": ["(",","]},"textDocumentSync": 1,"renameProvider":true,"completionProvider": {"resolveProvider": false,"triggerCharacters": [".",":","@"]},"documentHighlightProvider": false,"hoverProvider": true,"codeActionProvider": false,"declarationProvider": true,"definitionProvider": true,"typeDefinitionProvider": true,"implementationProvider": false,"referencesProvider": true,"documentSymbolProvider": true,"colorProvider": false,"documentFormattingProvider": true,"documentRangeFormattingProvider": false,"foldingRangeProvider": false,"selectionRangeProvider": false,"workspaceSymbolProvider": false,"rangeProvider": false,"documentProvider": true,"workspace": {"workspaceFolders": {"supported": true,"changeNotifications": true}},"semanticTokensProvider": {"documentProvider": true,"legend": {"tokenTypes": ["namespace","type","struct","enum","union","parameter","variable","tagField","field","errorTag","function","keyword","comment","string","number","operator","builtin","label"],"tokenModifiers": ["definition","async","documentation", "generic"]}}}}} ; const initialize_response = ",\"result\": {" ++ initialize_capabilities; diff --git a/src/references.zig b/src/references.zig new file mode 100644 index 0000000..46499e2 --- /dev/null +++ b/src/references.zig @@ -0,0 +1,410 @@ +const std = @import("std"); +const DocumentStore = @import("document_store.zig"); +const analysis = @import("analysis.zig"); +const types = @import("types.zig"); +const offsets = @import("offsets.zig"); + +const ast = std.zig.ast; + +fn tokenReference( + handle: *DocumentStore.Handle, + tok: ast.TokenIndex, + encoding: offsets.Encoding, + context: var, + comptime handler: var, +) !void { + const loc = offsets.tokenRelativeLocation(handle.tree, 0, tok, encoding) catch return; + try handler(context, types.Location{ + .uri = handle.uri(), + .range = .{ + .start = .{ + .line = @intCast(types.Integer, loc.line), + .character = @intCast(types.Integer, loc.column), + }, + .end = .{ + .line = @intCast(types.Integer, loc.line), + .character = @intCast(types.Integer, loc.column + offsets.tokenLength(handle.tree, tok, encoding)), + }, + }, + }); +} + +pub fn labelReferences( + arena: *std.heap.ArenaAllocator, + decl: analysis.DeclWithHandle, + encoding: offsets.Encoding, + include_decl: bool, + context: var, + comptime handler: var, +) !void { + std.debug.assert(decl.decl.* == .label_decl); + const handle = decl.handle; + + // Find while / for / block from label -> iterate over children nodes, find break and continues, change their labels if they match. + // This case can be implemented just by scanning tokens. + const first_tok = decl.decl.label_decl.firstToken(); + const last_tok = decl.decl.label_decl.lastToken(); + + if (include_decl) { + // The first token is always going to be the label + try tokenReference(handle, first_tok, encoding, context, handler); + } + + var curr_tok = first_tok + 1; + while (curr_tok < last_tok - 2) : (curr_tok += 1) { + const curr_id = handle.tree.token_ids[curr_tok]; + if ((curr_id == .Keyword_break or curr_id == .Keyword_continue) and handle.tree.token_ids[curr_tok + 1] == .Colon and + handle.tree.token_ids[curr_tok + 2] == .Identifier) + { + if (std.mem.eql(u8, handle.tree.tokenSlice(curr_tok + 2), handle.tree.tokenSlice(first_tok))) { + try tokenReference(handle, first_tok, encoding, context, handler); + } + } + } +} + +fn symbolReferencesInternal( + arena: *std.heap.ArenaAllocator, + store: *DocumentStore, + node_handle: analysis.NodeWithHandle, + decl: analysis.DeclWithHandle, + encoding: offsets.Encoding, + context: var, + comptime handler: var, +) error{OutOfMemory}!void { + const node = node_handle.node; + const handle = node_handle.handle; + + switch (node.id) { + .ContainerDecl, .Root, .Block => { + var idx: usize = 0; + while (node.iterate(idx)) |child| : (idx += 1) { + try symbolReferencesInternal(arena, store, .{ .node = child, .handle = handle }, decl, encoding, context, handler); + } + }, + .VarDecl => { + const var_decl = node.cast(ast.Node.VarDecl).?; + if (var_decl.type_node) |type_node| { + try symbolReferencesInternal(arena, store, .{ .node = type_node, .handle = handle }, decl, encoding, context, handler); + } + if (var_decl.init_node) |init_node| { + try symbolReferencesInternal(arena, store, .{ .node = init_node, .handle = handle }, decl, encoding, context, handler); + } + }, + .Use => { + const use = node.cast(ast.Node.Use).?; + try symbolReferencesInternal(arena, store, .{ .node = use.expr, .handle = handle }, decl, encoding, context, handler); + }, + .ContainerField => { + const field = node.cast(ast.Node.ContainerField).?; + if (field.type_expr) |type_node| { + try symbolReferencesInternal(arena, store, .{ .node = type_node, .handle = handle }, decl, encoding, context, handler); + } + if (field.value_expr) |init_node| { + try symbolReferencesInternal(arena, store, .{ .node = init_node, .handle = handle }, decl, encoding, context, handler); + } + }, + .Identifier => { + if (try analysis.lookupSymbolGlobal(store, arena, handle, handle.tree.getNodeSource(node), handle.tree.token_locs[node.firstToken()].start)) |child| { + if (std.meta.eql(decl, child)) { + try tokenReference(handle, node.firstToken(), encoding, context, handler); + } + } + }, + .FnProto => { + const fn_proto = node.cast(ast.Node.FnProto).?; + for (fn_proto.paramsConst()) |param| { + switch (param.param_type) { + .type_expr => |type_node| { + try symbolReferencesInternal(arena, store, .{ .node = type_node, .handle = handle }, decl, encoding, context, handler); + }, + else => {}, + } + } + switch (fn_proto.return_type) { + .Explicit, .InferErrorSet => |type_node| { + try symbolReferencesInternal(arena, store, .{ .node = type_node, .handle = handle }, decl, encoding, context, handler); + }, + else => {}, + } + if (fn_proto.align_expr) |align_expr| { + try symbolReferencesInternal(arena, store, .{ .node = align_expr, .handle = handle }, decl, encoding, context, handler); + } + if (fn_proto.section_expr) |section_expr| { + try symbolReferencesInternal(arena, store, .{ .node = section_expr, .handle = handle }, decl, encoding, context, handler); + } + if (fn_proto.callconv_expr) |callconv_expr| { + try symbolReferencesInternal(arena, store, .{ .node = callconv_expr, .handle = handle }, decl, encoding, context, handler); + } + if (fn_proto.body_node) |body| { + try symbolReferencesInternal(arena, store, .{ .node = body, .handle = handle }, decl, encoding, context, handler); + } + }, + .AnyFrameType => { + const anyframe_type = node.cast(ast.Node.AnyFrameType).?; + if (anyframe_type.result) |result| { + try symbolReferencesInternal(arena, store, .{ .node = result.return_type, .handle = handle }, decl, encoding, context, handler); + } + }, + .Defer => { + const defer_node = node.cast(ast.Node.Defer).?; + try symbolReferencesInternal(arena, store, .{ .node = defer_node.expr, .handle = handle }, decl, encoding, context, handler); + }, + .Comptime => { + const comptime_node = node.cast(ast.Node.Comptime).?; + try symbolReferencesInternal(arena, store, .{ .node = comptime_node.expr, .handle = handle }, decl, encoding, context, handler); + }, + .Nosuspend => { + const nosuspend_node = node.cast(ast.Node.Nosuspend).?; + try symbolReferencesInternal(arena, store, .{ .node = nosuspend_node.expr, .handle = handle }, decl, encoding, context, handler); + }, + .Switch => { + // TODO When renaming a union(enum) field, also rename switch items that refer to it. + const switch_node = node.cast(ast.Node.Switch).?; + try symbolReferencesInternal(arena, store, .{ .node = switch_node.expr, .handle = handle }, decl, encoding, context, handler); + for (switch_node.casesConst()) |case| { + if (case.*.cast(ast.Node.SwitchCase)) |case_node| { + try symbolReferencesInternal(arena, store, .{ .node = case_node.expr, .handle = handle }, decl, encoding, context, handler); + } + } + }, + .While => { + const while_node = node.cast(ast.Node.While).?; + try symbolReferencesInternal(arena, store, .{ .node = while_node.condition, .handle = handle }, decl, encoding, context, handler); + if (while_node.continue_expr) |cont_expr| { + try symbolReferencesInternal(arena, store, .{ .node = cont_expr, .handle = handle }, decl, encoding, context, handler); + } + try symbolReferencesInternal(arena, store, .{ .node = while_node.body, .handle = handle }, decl, encoding, context, handler); + if (while_node.@"else") |else_node| { + try symbolReferencesInternal(arena, store, .{ .node = else_node.body, .handle = handle }, decl, encoding, context, handler); + } + }, + .For => { + const for_node = node.cast(ast.Node.For).?; + try symbolReferencesInternal(arena, store, .{ .node = for_node.array_expr, .handle = handle }, decl, encoding, context, handler); + try symbolReferencesInternal(arena, store, .{ .node = for_node.body, .handle = handle }, decl, encoding, context, handler); + if (for_node.@"else") |else_node| { + try symbolReferencesInternal(arena, store, .{ .node = else_node.body, .handle = handle }, decl, encoding, context, handler); + } + }, + .If => { + const if_node = node.cast(ast.Node.If).?; + try symbolReferencesInternal(arena, store, .{ .node = if_node.condition, .handle = handle }, decl, encoding, context, handler); + try symbolReferencesInternal(arena, store, .{ .node = if_node.body, .handle = handle }, decl, encoding, context, handler); + if (if_node.@"else") |else_node| { + try symbolReferencesInternal(arena, store, .{ .node = else_node.body, .handle = handle }, decl, encoding, context, handler); + } + }, + .InfixOp => { + const infix_op = node.cast(ast.Node.InfixOp).?; + switch (infix_op.op) { + .Period => { + try symbolReferencesInternal(arena, store, .{ .node = infix_op.lhs, .handle = handle }, decl, encoding, context, handler); + + const rhs_str = analysis.nodeToString(handle.tree, infix_op.rhs) orelse return; + var bound_type_params = analysis.BoundTypeParams.init(&arena.allocator); + const left_type = try analysis.resolveFieldAccessLhsType( + store, + arena, + (try analysis.resolveTypeOfNodeInternal(store, arena, .{ + .node = infix_op.lhs, + .handle = handle, + }, &bound_type_params)) orelse return, + &bound_type_params, + ); + + const left_type_node = switch (left_type.type.data) { + .other => |n| n, + else => return, + }; + + if (try analysis.lookupSymbolContainer( + store, + arena, + .{ .node = left_type_node, .handle = left_type.handle }, + rhs_str, + !left_type.type.is_type_val, + )) |child| { + if (std.meta.eql(child, decl)) { + try tokenReference(handle, infix_op.rhs.firstToken(), encoding, context, handler); + } + } + }, + else => { + try symbolReferencesInternal(arena, store, .{ .node = infix_op.lhs, .handle = handle }, decl, encoding, context, handler); + try symbolReferencesInternal(arena, store, .{ .node = infix_op.rhs, .handle = handle }, decl, encoding, context, handler); + }, + } + }, + .PrefixOp => { + const prefix_op = node.cast(ast.Node.PrefixOp).?; + switch (prefix_op.op) { + .ArrayType => |info| { + try symbolReferencesInternal(arena, store, .{ .node = info.len_expr, .handle = handle }, decl, encoding, context, handler); + if (info.sentinel) |sentinel| { + try symbolReferencesInternal(arena, store, .{ .node = sentinel, .handle = handle }, decl, encoding, context, handler); + } + }, + .PtrType, .SliceType => |info| { + if (info.align_info) |align_info| { + try symbolReferencesInternal(arena, store, .{ .node = align_info.node, .handle = handle }, decl, encoding, context, handler); + if (align_info.bit_range) |range| { + try symbolReferencesInternal(arena, store, .{ .node = range.start, .handle = handle }, decl, encoding, context, handler); + try symbolReferencesInternal(arena, store, .{ .node = range.end, .handle = handle }, decl, encoding, context, handler); + } + } + if (info.sentinel) |sentinel| { + try symbolReferencesInternal(arena, store, .{ .node = sentinel, .handle = handle }, decl, encoding, context, handler); + } + }, + else => {}, + } + try symbolReferencesInternal(arena, store, .{ .node = prefix_op.rhs, .handle = handle }, decl, encoding, context, handler); + }, + .FieldInitializer => { + // TODO Rename field initializer names when needed + const field_init = node.cast(ast.Node.FieldInitializer).?; + try symbolReferencesInternal(arena, store, .{ .node = field_init.expr, .handle = handle }, decl, encoding, context, handler); + }, + .ArrayInitializer => { + const array_init = node.cast(ast.Node.ArrayInitializer).?; + try symbolReferencesInternal(arena, store, .{ .node = array_init.lhs, .handle = handle }, decl, encoding, context, handler); + for (array_init.listConst()) |child| { + try symbolReferencesInternal(arena, store, .{ .node = child, .handle = handle }, decl, encoding, context, handler); + } + }, + .ArrayInitializerDot => { + const array_init = node.cast(ast.Node.ArrayInitializerDot).?; + for (array_init.listConst()) |child| { + try symbolReferencesInternal(arena, store, .{ .node = child, .handle = handle }, decl, encoding, context, handler); + } + }, + .StructInitializer => { + // TODO Rename field initializer names when needed + const struct_init = node.cast(ast.Node.StructInitializer).?; + try symbolReferencesInternal(arena, store, .{ .node = struct_init.lhs, .handle = handle }, decl, encoding, context, handler); + for (struct_init.listConst()) |child| { + try symbolReferencesInternal(arena, store, .{ .node = child, .handle = handle }, decl, encoding, context, handler); + } + }, + .StructInitializerDot => { + const struct_init = node.cast(ast.Node.StructInitializerDot).?; + for (struct_init.listConst()) |child| { + try symbolReferencesInternal(arena, store, .{ .node = child, .handle = handle }, decl, encoding, context, handler); + } + }, + .Call => { + const call = node.cast(ast.Node.Call).?; + try symbolReferencesInternal(arena, store, .{ .node = call.lhs, .handle = handle }, decl, encoding, context, handler); + for (call.paramsConst()) |param| { + try symbolReferencesInternal(arena, store, .{ .node = param, .handle = handle }, decl, encoding, context, handler); + } + }, + .SuffixOp => { + const suffix_op = node.cast(ast.Node.SuffixOp).?; + try symbolReferencesInternal(arena, store, .{ .node = suffix_op.lhs, .handle = handle }, decl, encoding, context, handler); + switch (suffix_op.op) { + .ArrayAccess => |acc| try symbolReferencesInternal(arena, store, .{ .node = acc, .handle = handle }, decl, encoding, context, handler), + .Slice => |sl| { + try symbolReferencesInternal(arena, store, .{ .node = sl.start, .handle = handle }, decl, encoding, context, handler); + if (sl.end) |end| { + try symbolReferencesInternal(arena, store, .{ .node = end, .handle = handle }, decl, encoding, context, handler); + } + if (sl.sentinel) |sentinel| { + try symbolReferencesInternal(arena, store, .{ .node = sentinel, .handle = handle }, decl, encoding, context, handler); + } + }, + else => {}, + } + }, + .GroupedExpression => { + const grouped = node.cast(ast.Node.GroupedExpression).?; + try symbolReferencesInternal(arena, store, .{ .node = grouped.expr, .handle = handle }, decl, encoding, context, handler); + }, + .ControlFlowExpression => { + const cfe = node.cast(ast.Node.ControlFlowExpression).?; + if (cfe.rhs) |rhs| { + try symbolReferencesInternal(arena, store, .{ .node = rhs, .handle = handle }, decl, encoding, context, handler); + } + }, + .Suspend => { + const suspend_node = node.cast(ast.Node.Suspend).?; + if (suspend_node.body) |body| { + try symbolReferencesInternal(arena, store, .{ .node = body, .handle = handle }, decl, encoding, context, handler); + } + }, + .BuiltinCall => { + const builtin_call = node.cast(ast.Node.BuiltinCall).?; + for (builtin_call.paramsConst()) |param| { + try symbolReferencesInternal(arena, store, .{ .node = param, .handle = handle }, decl, encoding, context, handler); + } + }, + // TODO Inline asm expr + .TestDecl => { + const test_decl = node.cast(ast.Node.TestDecl).?; + try symbolReferencesInternal(arena, store, .{ .node = test_decl.body_node, .handle = handle }, decl, encoding, context, handler); + }, + else => {}, + } +} + +pub fn symbolReferences( + arena: *std.heap.ArenaAllocator, + store: *DocumentStore, + decl_handle: analysis.DeclWithHandle, + encoding: offsets.Encoding, + include_decl: bool, + context: var, + comptime handler: var, +) !void { + std.debug.assert(decl_handle.decl.* != .label_decl); + const curr_handle = decl_handle.handle; + + switch (decl_handle.decl.*) { + .ast_node => |decl_node| { + var handles = std.ArrayList(*DocumentStore.Handle).init(&arena.allocator); + var handle_it = store.handles.iterator(); + while (handle_it.next()) |entry| { + 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 = &handle.tree.root_node.base, .handle = handle }, decl_handle, encoding, context, handler); + } + }, + .param_decl => |param| { + // Rename the param tok. + if (include_decl) { + try tokenReference(curr_handle, decl_handle.nameToken(), encoding, context, handler); + } + const fn_node = loop: for (curr_handle.document_scope.scopes) |scope| { + switch (scope.data) { + .function => |proto| { + const fn_proto = proto.cast(std.zig.ast.Node.FnProto).?; + for (fn_proto.paramsConst()) |*candidate| { + if (candidate == param) + break :loop fn_proto; + } + }, + else => {}, + } + } else { + std.log.warn(.references, "Could not find param decl's function", .{}); + return; + }; + if (fn_node.body_node) |body| { + try symbolReferencesInternal(arena, store, .{ .node = body, .handle = curr_handle }, decl_handle, encoding, context, handler); + } + }, + .pointer_payload, .array_payload, .switch_payload => { + if (include_decl) { + try tokenReference(curr_handle, decl_handle.nameToken(), encoding, context, handler); + } + try symbolReferencesInternal(arena, store, .{ .node = &curr_handle.tree.root_node.base, .handle = curr_handle }, decl_handle, encoding, context, handler); + }, + .label_decl => unreachable, + } +} diff --git a/src/rename.zig b/src/rename.zig index 237abf0..c12e8b7 100644 --- a/src/rename.zig +++ b/src/rename.zig @@ -1,340 +1,30 @@ const std = @import("std"); const DocumentStore = @import("document_store.zig"); const analysis = @import("analysis.zig"); +const references = @import("references.zig"); const types = @import("types.zig"); const offsets = @import("offsets.zig"); const ast = std.zig.ast; -fn renameToken(handle: *DocumentStore.Handle, tok: ast.TokenIndex, new_name: []const u8, edits: *std.ArrayList(types.TextEdit), encoding: offsets.Encoding) !void { - const loc = offsets.tokenRelativeLocation(handle.tree, 0, tok, encoding) catch return; - (try edits.addOne()).* = .{ - .range = .{ - .start = .{ - .line = @intCast(types.Integer, loc.line), - .character = @intCast(types.Integer, loc.column), - }, - .end = .{ - .line = @intCast(types.Integer, loc.line), - .character = @intCast(types.Integer, loc.column + offsets.tokenLength(handle.tree, tok, encoding)), - }, - }, - .newText = new_name, - }; -} - -pub fn renameLabel(arena: *std.heap.ArenaAllocator, decl: analysis.DeclWithHandle, new_name: []const u8, edits: *std.StringHashMap([]types.TextEdit), encoding: offsets.Encoding) !void { - std.debug.assert(decl.decl.* == .label_decl); - const handle = decl.handle; - - var text_edits = std.ArrayList(types.TextEdit).init(&arena.allocator); - - // Find while / for / block from label -> iterate over children nodes, find break and continues, change their labels if they match. - // This case can be implemented just by scanning tokens. - const first_tok = decl.decl.label_decl.firstToken(); - const last_tok = decl.decl.label_decl.lastToken(); - - // The first token is always going to be the label - try renameToken(handle, first_tok, new_name, &text_edits, encoding); - - var curr_tok = first_tok + 1; - while (curr_tok < last_tok - 2) : (curr_tok += 1) { - const curr_id = handle.tree.token_ids[curr_tok]; - if ((curr_id == .Keyword_break or curr_id == .Keyword_continue) and handle.tree.token_ids[curr_tok + 1] == .Colon and - handle.tree.token_ids[curr_tok + 2] == .Identifier) - { - if (std.mem.eql(u8, handle.tree.tokenSlice(curr_tok + 2), handle.tree.tokenSlice(first_tok))) { - try renameToken(handle, curr_tok + 2, new_name, &text_edits, encoding); - } - } - } - - try edits.putNoClobber(handle.uri(), text_edits.items); -} - -fn renameSymbolInternal( - arena: *std.heap.ArenaAllocator, - store: *DocumentStore, - node_handle: analysis.NodeWithHandle, - decl: analysis.DeclWithHandle, +// TODO Use an map to array lists and collect at the end instead? +const RefHandlerContext = struct { + edits: *std.StringHashMap([]types.TextEdit), + allocator: *std.mem.Allocator, new_name: []const u8, - edits: *std.ArrayList(types.TextEdit), - encoding: offsets.Encoding, -) error{OutOfMemory}!void { - const node = node_handle.node; - const handle = node_handle.handle; +}; - switch (node.id) { - .ContainerDecl, .Root, .Block => { - var idx: usize = 0; - while (node.iterate(idx)) |child| : (idx += 1) { - try renameSymbolInternal(arena, store, .{ .node = child, .handle = handle }, decl, new_name, edits, encoding); - } - }, - .VarDecl => { - const var_decl = node.cast(ast.Node.VarDecl).?; - if (var_decl.type_node) |type_node| { - try renameSymbolInternal(arena, store, .{ .node = type_node, .handle = handle }, decl, new_name, edits, encoding); - } - if (var_decl.init_node) |init_node| { - try renameSymbolInternal(arena, store, .{ .node = init_node, .handle = handle }, decl, new_name, edits, encoding); - } - }, - .Use => { - const use = node.cast(ast.Node.Use).?; - try renameSymbolInternal(arena, store, .{ .node = use.expr, .handle = handle }, decl, new_name, edits, encoding); - }, - .ContainerField => { - const field = node.cast(ast.Node.ContainerField).?; - if (field.type_expr) |type_node| { - try renameSymbolInternal(arena, store, .{ .node = type_node, .handle = handle }, decl, new_name, edits, encoding); - } - if (field.value_expr) |init_node| { - try renameSymbolInternal(arena, store, .{ .node = init_node, .handle = handle }, decl, new_name, edits, encoding); - } - }, - .Identifier => { - if (try analysis.lookupSymbolGlobal(store, arena, handle, handle.tree.getNodeSource(node), handle.tree.token_locs[node.firstToken()].start)) |child| { - if (std.meta.eql(decl, child)) { - try renameToken(handle, node.firstToken(), new_name, edits, encoding); - } - } - }, - .FnProto => { - const fn_proto = node.cast(ast.Node.FnProto).?; - for (fn_proto.paramsConst()) |param| { - switch (param.param_type) { - .type_expr => |type_node| { - try renameSymbolInternal(arena, store, .{ .node = type_node, .handle = handle }, decl, new_name, edits, encoding); - }, - else => {}, - } - } - switch (fn_proto.return_type) { - .Explicit, .InferErrorSet => |type_node| { - try renameSymbolInternal(arena, store, .{ .node = type_node, .handle = handle }, decl, new_name, edits, encoding); - }, - else => {}, - } - if (fn_proto.align_expr) |align_expr| { - try renameSymbolInternal(arena, store, .{ .node = align_expr, .handle = handle }, decl, new_name, edits, encoding); - } - if (fn_proto.section_expr) |section_expr| { - try renameSymbolInternal(arena, store, .{ .node = section_expr, .handle = handle }, decl, new_name, edits, encoding); - } - if (fn_proto.callconv_expr) |callconv_expr| { - try renameSymbolInternal(arena, store, .{ .node = callconv_expr, .handle = handle }, decl, new_name, edits, encoding); - } - if (fn_proto.body_node) |body| { - try renameSymbolInternal(arena, store, .{ .node = body, .handle = handle }, decl, new_name, edits, encoding); - } - }, - .AnyFrameType => { - const anyframe_type = node.cast(ast.Node.AnyFrameType).?; - if (anyframe_type.result) |result| { - try renameSymbolInternal(arena, store, .{ .node = result.return_type, .handle = handle }, decl, new_name, edits, encoding); - } - }, - .Defer => { - const defer_node = node.cast(ast.Node.Defer).?; - try renameSymbolInternal(arena, store, .{ .node = defer_node.expr, .handle = handle }, decl, new_name, edits, encoding); - }, - .Comptime => { - const comptime_node = node.cast(ast.Node.Comptime).?; - try renameSymbolInternal(arena, store, .{ .node = comptime_node.expr, .handle = handle }, decl, new_name, edits, encoding); - }, - .Nosuspend => { - const nosuspend_node = node.cast(ast.Node.Nosuspend).?; - try renameSymbolInternal(arena, store, .{ .node = nosuspend_node.expr, .handle = handle }, decl, new_name, edits, encoding); - }, - .Switch => { - // TODO When renaming a union(enum) field, also rename switch items that refer to it. - const switch_node = node.cast(ast.Node.Switch).?; - try renameSymbolInternal(arena, store, .{ .node = switch_node.expr, .handle = handle }, decl, new_name, edits, encoding); - for (switch_node.casesConst()) |case| { - if (case.*.cast(ast.Node.SwitchCase)) |case_node| { - try renameSymbolInternal(arena, store, .{ .node = case_node.expr, .handle = handle }, decl, new_name, edits, encoding); - } - } - }, - .While => { - const while_node = node.cast(ast.Node.While).?; - try renameSymbolInternal(arena, store, .{ .node = while_node.condition, .handle = handle }, decl, new_name, edits, encoding); - if (while_node.continue_expr) |cont_expr| { - try renameSymbolInternal(arena, store, .{ .node = cont_expr, .handle = handle }, decl, new_name, edits, encoding); - } - try renameSymbolInternal(arena, store, .{ .node = while_node.body, .handle = handle }, decl, new_name, edits, encoding); - if (while_node.@"else") |else_node| { - try renameSymbolInternal(arena, store, .{ .node = else_node.body, .handle = handle }, decl, new_name, edits, encoding); - } - }, - .For => { - const for_node = node.cast(ast.Node.For).?; - try renameSymbolInternal(arena, store, .{ .node = for_node.array_expr, .handle = handle }, decl, new_name, edits, encoding); - try renameSymbolInternal(arena, store, .{ .node = for_node.body, .handle = handle }, decl, new_name, edits, encoding); - if (for_node.@"else") |else_node| { - try renameSymbolInternal(arena, store, .{ .node = else_node.body, .handle = handle }, decl, new_name, edits, encoding); - } - }, - .If => { - const if_node = node.cast(ast.Node.If).?; - try renameSymbolInternal(arena, store, .{ .node = if_node.condition, .handle = handle }, decl, new_name, edits, encoding); - try renameSymbolInternal(arena, store, .{ .node = if_node.body, .handle = handle }, decl, new_name, edits, encoding); - if (if_node.@"else") |else_node| { - try renameSymbolInternal(arena, store, .{ .node = else_node.body, .handle = handle }, decl, new_name, edits, encoding); - } - }, - .InfixOp => { - const infix_op = node.cast(ast.Node.InfixOp).?; - switch (infix_op.op) { - .Period => { - try renameSymbolInternal(arena, store, .{ .node = infix_op.lhs, .handle = handle }, decl, new_name, edits, encoding); +fn refHandler(context: RefHandlerContext, loc: types.Location) !void { + var text_edits = if (context.edits.getValue(loc.uri)) |slice| + std.ArrayList(types.TextEdit).fromOwnedSlice(context.allocator, slice) + else + std.ArrayList(types.TextEdit).init(context.allocator); - const rhs_str = analysis.nodeToString(handle.tree, infix_op.rhs) orelse return; - var bound_type_params = analysis.BoundTypeParams.init(&arena.allocator); - const left_type = try analysis.resolveFieldAccessLhsType( - store, - arena, - (try analysis.resolveTypeOfNodeInternal(store, arena, .{ - .node = infix_op.lhs, - .handle = handle, - }, &bound_type_params)) orelse return, - &bound_type_params, - ); - - const left_type_node = switch (left_type.type.data) { - .other => |n| n, - else => return, - }; - - if (try analysis.lookupSymbolContainer( - store, - arena, - .{ .node = left_type_node, .handle = left_type.handle }, - rhs_str, - !left_type.type.is_type_val, - )) |child| { - if (std.meta.eql(child, decl)) { - try renameToken(handle, infix_op.rhs.firstToken(), new_name, edits, encoding); - } - } - }, - else => { - try renameSymbolInternal(arena, store, .{ .node = infix_op.lhs, .handle = handle }, decl, new_name, edits, encoding); - try renameSymbolInternal(arena, store, .{ .node = infix_op.rhs, .handle = handle }, decl, new_name, edits, encoding); - }, - } - }, - .PrefixOp => { - const prefix_op = node.cast(ast.Node.PrefixOp).?; - switch (prefix_op.op) { - .ArrayType => |info| { - try renameSymbolInternal(arena, store, .{ .node = info.len_expr, .handle = handle }, decl, new_name, edits, encoding); - if (info.sentinel) |sentinel| { - try renameSymbolInternal(arena, store, .{ .node = sentinel, .handle = handle }, decl, new_name, edits, encoding); - } - }, - .PtrType, .SliceType => |info| { - if (info.align_info) |align_info| { - try renameSymbolInternal(arena, store, .{ .node = align_info.node, .handle = handle }, decl, new_name, edits, encoding); - if (align_info.bit_range) |range| { - try renameSymbolInternal(arena, store, .{ .node = range.start, .handle = handle }, decl, new_name, edits, encoding); - try renameSymbolInternal(arena, store, .{ .node = range.end, .handle = handle }, decl, new_name, edits, encoding); - } - } - if (info.sentinel) |sentinel| { - try renameSymbolInternal(arena, store, .{ .node = sentinel, .handle = handle }, decl, new_name, edits, encoding); - } - }, - else => {}, - } - try renameSymbolInternal(arena, store, .{ .node = prefix_op.rhs, .handle = handle }, decl, new_name, edits, encoding); - }, - .FieldInitializer => { - // TODO Rename field initializer names when needed - const field_init = node.cast(ast.Node.FieldInitializer).?; - try renameSymbolInternal(arena, store, .{ .node = field_init.expr, .handle = handle }, decl, new_name, edits, encoding); - }, - .ArrayInitializer => { - const array_init = node.cast(ast.Node.ArrayInitializer).?; - try renameSymbolInternal(arena, store, .{ .node = array_init.lhs, .handle = handle }, decl, new_name, edits, encoding); - for (array_init.listConst()) |child| { - try renameSymbolInternal(arena, store, .{ .node = child, .handle = handle }, decl, new_name, edits, encoding); - } - }, - .ArrayInitializerDot => { - const array_init = node.cast(ast.Node.ArrayInitializerDot).?; - for (array_init.listConst()) |child| { - try renameSymbolInternal(arena, store, .{ .node = child, .handle = handle }, decl, new_name, edits, encoding); - } - }, - .StructInitializer => { - // TODO Rename field initializer names when needed - const struct_init = node.cast(ast.Node.StructInitializer).?; - try renameSymbolInternal(arena, store, .{ .node = struct_init.lhs, .handle = handle }, decl, new_name, edits, encoding); - for (struct_init.listConst()) |child| { - try renameSymbolInternal(arena, store, .{ .node = child, .handle = handle }, decl, new_name, edits, encoding); - } - }, - .StructInitializerDot => { - const struct_init = node.cast(ast.Node.StructInitializerDot).?; - for (struct_init.listConst()) |child| { - try renameSymbolInternal(arena, store, .{ .node = child, .handle = handle }, decl, new_name, edits, encoding); - } - }, - .Call => { - const call = node.cast(ast.Node.Call).?; - try renameSymbolInternal(arena, store, .{ .node = call.lhs, .handle = handle }, decl, new_name, edits, encoding); - for (call.paramsConst()) |param| { - try renameSymbolInternal(arena, store, .{ .node = param, .handle = handle }, decl, new_name, edits, encoding); - } - }, - .SuffixOp => { - const suffix_op = node.cast(ast.Node.SuffixOp).?; - try renameSymbolInternal(arena, store, .{ .node = suffix_op.lhs, .handle = handle }, decl, new_name, edits, encoding); - switch (suffix_op.op) { - .ArrayAccess => |acc| try renameSymbolInternal(arena, store, .{ .node = acc, .handle = handle }, decl, new_name, edits, encoding), - .Slice => |sl| { - try renameSymbolInternal(arena, store, .{ .node = sl.start, .handle = handle }, decl, new_name, edits, encoding); - if (sl.end) |end| { - try renameSymbolInternal(arena, store, .{ .node = end, .handle = handle }, decl, new_name, edits, encoding); - } - if (sl.sentinel) |sentinel| { - try renameSymbolInternal(arena, store, .{ .node = sentinel, .handle = handle }, decl, new_name, edits, encoding); - } - }, - else => {}, - } - }, - .GroupedExpression => { - const grouped = node.cast(ast.Node.GroupedExpression).?; - try renameSymbolInternal(arena, store, .{ .node = grouped.expr, .handle = handle }, decl, new_name, edits, encoding); - }, - .ControlFlowExpression => { - const cfe = node.cast(ast.Node.ControlFlowExpression).?; - if (cfe.rhs) |rhs| { - try renameSymbolInternal(arena, store, .{ .node = rhs, .handle = handle }, decl, new_name, edits, encoding); - } - }, - .Suspend => { - const suspend_node = node.cast(ast.Node.Suspend).?; - if (suspend_node.body) |body| { - try renameSymbolInternal(arena, store, .{ .node = body, .handle = handle }, decl, new_name, edits, encoding); - } - }, - .BuiltinCall => { - const builtin_call = node.cast(ast.Node.BuiltinCall).?; - for (builtin_call.paramsConst()) |param| { - try renameSymbolInternal(arena, store, .{ .node = param, .handle = handle }, decl, new_name, edits, encoding); - } - }, - // TODO Inline asm expr - .TestDecl => { - const test_decl = node.cast(ast.Node.TestDecl).?; - try renameSymbolInternal(arena, store, .{ .node = test_decl.body_node, .handle = handle }, decl, new_name, edits, encoding); - }, - else => {}, - } + (try text_edits.addOne()).* = .{ + .range = loc.range, + .newText = context.new_name, + }; + _ = try context.edits.put(loc.uri, text_edits.toOwnedSlice()); } pub fn renameSymbol( @@ -346,57 +36,24 @@ pub fn renameSymbol( encoding: offsets.Encoding, ) !void { std.debug.assert(decl_handle.decl.* != .label_decl); - const curr_handle = decl_handle.handle; - - switch (decl_handle.decl.*) { - .ast_node => |decl_node| { - var handles = std.ArrayList(*DocumentStore.Handle).init(&arena.allocator); - var handle_it = store.handles.iterator(); - while (handle_it.next()) |entry| { - try handles.append(entry.value); - } - for (handles.items) |handle| { - var text_edits = std.ArrayList(types.TextEdit).init(&arena.allocator); - if (handle == curr_handle) { - try renameToken(curr_handle, decl_handle.nameToken(), new_name, &text_edits, encoding); - } - - try renameSymbolInternal(arena, store, .{ .node = &handle.tree.root_node.base, .handle = handle }, decl_handle, new_name, &text_edits, encoding); - if (text_edits.items.len > 0) { - try edits.putNoClobber(handle.uri(), text_edits.items); - } - } - }, - .param_decl => |param| { - var curr_doc_text_edits = std.ArrayList(types.TextEdit).init(&arena.allocator); - // Rename the param tok. - try renameToken(curr_handle, decl_handle.nameToken(), new_name, &curr_doc_text_edits, encoding); - const fn_node = loop: for (curr_handle.document_scope.scopes) |scope| { - switch (scope.data) { - .function => |proto| { - const fn_proto = proto.cast(std.zig.ast.Node.FnProto).?; - for (fn_proto.paramsConst()) |*candidate| { - if (candidate == param) - break :loop fn_proto; - } - }, - else => {}, - } - } else { - std.log.warn(.rename, "Could not find param decl's function", .{}); - return; - }; - if (fn_node.body_node) |body| { - try renameSymbolInternal(arena, store, .{ .node = body, .handle = curr_handle }, decl_handle, new_name, &curr_doc_text_edits, encoding); - } - try edits.putNoClobber(curr_handle.uri(), curr_doc_text_edits.items); - }, - .pointer_payload, .array_payload, .switch_payload => { - var curr_doc_text_edits = std.ArrayList(types.TextEdit).init(&arena.allocator); - try renameToken(curr_handle, decl_handle.nameToken(), new_name, &curr_doc_text_edits, encoding); - try renameSymbolInternal(arena, store, .{ .node = &curr_handle.tree.root_node.base, .handle = curr_handle }, decl_handle, new_name, &curr_doc_text_edits, encoding); - try edits.putNoClobber(curr_handle.uri(), curr_doc_text_edits.items); - }, - .label_decl => unreachable, - } + try references.symbolReferences(arena, store, decl_handle, encoding, true, RefHandlerContext{ + .edits = edits, + .allocator = &arena.allocator, + .new_name = new_name, + }, refHandler); +} + +pub fn renameLabel( + arena: *std.heap.ArenaAllocator, + decl_handle: analysis.DeclWithHandle, + new_name: []const u8, + edits: *std.StringHashMap([]types.TextEdit), + encoding: offsets.Encoding, +) !void { + std.debug.assert(decl_handle.decl.* == .label_decl); + try references.labelReferences(arena, decl_handle, encoding, true, RefHandlerContext{ + .edits = edits, + .allocator = &arena.allocator, + .new_name = new_name, + }, refHandler); } diff --git a/src/requests.zig b/src/requests.zig index 84e2e80..58bb4fb 100644 --- a/src/requests.zig +++ b/src/requests.zig @@ -208,3 +208,11 @@ pub const Rename = struct { newName: types.String, }, }; + +pub const Reference = struct { + params: struct { + textDocument: TextDocumentIdentifier, + position: types.Position, + includeDeclaration: bool, + }, +};