From c6da7082508082de47b2ee452b13e9f2104aa3c4 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Fri, 7 Nov 2025 11:55:26 +0100 Subject: [PATCH] refactor: add project_manager.highlight_references request --- src/Project.zig | 53 ++++++++++++++++++++++++++++++++++------- src/project_manager.zig | 16 +++++++++++++ 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/src/Project.zig b/src/Project.zig index e1ce93e..06e5712 100644 --- a/src/Project.zig +++ b/src/Project.zig @@ -915,7 +915,7 @@ fn send_goto_request(self: *Self, from: tp.pid_ref, file_path: []const u8, row: if (try cbor.match(response.buf, .{ tp.any, tp.any, tp.any, .{tp.extract_cbor(&link)} })) { try navigate_to_location_link(self_.from.ref(), link); } else if (try cbor.match(response.buf, .{ tp.any, tp.any, tp.any, tp.extract_cbor(&locations) })) { - try self_.project.send_reference_list(self_.from.ref(), locations, self_.name); + _ = try send_reference_list("REF", self_.from.ref(), locations, self_.name); } } else if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.null_ })) { return; @@ -1014,7 +1014,8 @@ pub fn references(self: *Self, from: tp.pid_ref, file_path: []const u8, row: usi if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.null_ })) { return; } else if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.extract_cbor(&locations) })) { - try self_.project.send_reference_list(self_.from.ref(), locations, self_.name); + const count = try send_reference_list("REF", self_.from.ref(), locations, self_.name); + self_.project.logger_lsp.print("found {d} references", .{count}); } } } = .{ @@ -1030,21 +1031,57 @@ pub fn references(self: *Self, from: tp.pid_ref, file_path: []const u8, row: usi }, handler) catch return error.LspFailed; } -fn send_reference_list(self: *Self, to: tp.pid_ref, locations: []const u8, name: []const u8) (ClientError || InvalidMessageError || GetLineOfFileError || cbor.Error)!void { - defer to.send(.{ "REF", "done" }) catch {}; +pub fn highlight_references(self: *Self, from: tp.pid_ref, file_path: []const u8, row: usize, col: usize) SendGotoRequestError!void { + const lsp = try self.get_language_server(file_path); + const uri = try self.make_URI(file_path); + defer self.allocator.free(uri); + + const handler: struct { + from: tp.pid, + name: []const u8, + project: *Self, + + pub fn deinit(self_: *@This()) void { + std.heap.c_allocator.free(self_.name); + self_.from.deinit(); + } + + pub fn receive(self_: @This(), response: tp.message) !void { + var locations: []const u8 = undefined; + if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.null_ })) { + return; + } else if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.extract_cbor(&locations) })) { + _ = try send_reference_list("HREF", self_.from.ref(), locations, self_.name); + } + } + } = .{ + .from = from.clone(), + .name = try std.heap.c_allocator.dupe(u8, self.name), + .project = self, + }; + + lsp.send_request(self.allocator, "textDocument/references", .{ + .textDocument = .{ .uri = uri }, + .position = .{ .line = row, .character = col }, + .context = .{ .includeDeclaration = true }, + }, handler) catch return error.LspFailed; +} + +fn send_reference_list(tag: []const u8, to: tp.pid_ref, locations: []const u8, name: []const u8) (ClientError || InvalidMessageError || GetLineOfFileError || cbor.Error)!usize { + defer to.send(.{ tag, "done" }) catch {}; var iter = locations; var len = try cbor.decodeArrayHeader(&iter); const count = len; while (len > 0) : (len -= 1) { var location: []const u8 = undefined; if (try cbor.matchValue(&iter, cbor.extract_cbor(&location))) { - try send_reference(to, location, name); + try send_reference(tag, to, location, name); } else return error.InvalidMessageField; } - self.logger_lsp.print("found {d} references", .{count}); + return count; } -fn send_reference(to: tp.pid_ref, location: []const u8, name: []const u8) (ClientError || InvalidMessageError || GetLineOfFileError || cbor.Error)!void { +fn send_reference(tag: []const u8, to: tp.pid_ref, location: []const u8, name: []const u8) (ClientError || InvalidMessageError || GetLineOfFileError || cbor.Error)!void { const allocator = std.heap.c_allocator; var iter = location; var targetUri: ?[]const u8 = null; @@ -1087,7 +1124,7 @@ fn send_reference(to: tp.pid_ref, location: []const u8, name: []const u8) (Clien else file_path; to.send(.{ - "REF", + tag, file_path_, targetRange.?.start.line + 1, targetRange.?.start.character, diff --git a/src/project_manager.zig b/src/project_manager.zig index 4cd1146..0b27dc9 100644 --- a/src/project_manager.zig +++ b/src/project_manager.zig @@ -234,6 +234,13 @@ pub fn references(file_path: []const u8, row: usize, col: usize) (ProjectManager return send(.{ "references", project, file_path, row, col }); } +pub fn highlight_references(file_path: []const u8, row: usize, col: usize) (ProjectManagerError || ProjectError)!void { + const project = tp.env.get().str("project"); + if (project.len == 0) + return error.NoProject; + return send(.{ "highlight_references", project, file_path, row, col }); +} + pub fn completion(file_path: []const u8, row: usize, col: usize) (ProjectManagerError || ProjectError)!void { const project = tp.env.get().str("project"); if (project.len == 0) @@ -432,6 +439,8 @@ const Process = struct { self.goto_type_definition(from, project_directory, path, row, col) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed; } else if (try cbor.match(m.buf, .{ "references", tp.extract(&project_directory), tp.extract(&path), tp.extract(&row), tp.extract(&col) })) { self.references(from, project_directory, path, row, col) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed; + } else if (try cbor.match(m.buf, .{ "highlight_references", tp.extract(&project_directory), tp.extract(&path), tp.extract(&row), tp.extract(&col) })) { + self.highlight_references(from, project_directory, path, row, col) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed; } else if (try cbor.match(m.buf, .{ "completion", tp.extract(&project_directory), tp.extract(&path), tp.extract(&row), tp.extract(&col) })) { self.completion(from, project_directory, path, row, col) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed; } else if (try cbor.match(m.buf, .{ "rename_symbol", tp.extract(&project_directory), tp.extract(&path), tp.extract(&row), tp.extract(&col) })) { @@ -661,6 +670,13 @@ const Process = struct { return project.references(from, file_path, row, col); } + fn highlight_references(self: *Process, from: tp.pid_ref, project_directory: []const u8, file_path: []const u8, row: usize, col: usize) (ProjectError || Project.SendGotoRequestError)!void { + const frame = tracy.initZone(@src(), .{ .name = module_name ++ ".highlight_references" }); + defer frame.deinit(); + const project = self.projects.get(project_directory) orelse return error.NoProject; + return project.highlight_references(from, file_path, row, col); + } + fn completion(self: *Process, from: tp.pid_ref, project_directory: []const u8, file_path: []const u8, row: usize, col: usize) (ProjectError || Project.InvalidMessageError || Project.LspOrClientError || cbor.Error)!void { const frame = tracy.initZone(@src(), .{ .name = module_name ++ ".completion" }); defer frame.deinit();