From c021136fef46f9399e7c63c642342db4fe19fc50 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 17 Dec 2025 21:01:02 +0100 Subject: [PATCH] refactor: add request_vcs_id and request_vcs_content to project_manager --- src/Project.zig | 70 +++++++++++++++++++++++++++++++++++++++++ src/project_manager.zig | 35 +++++++++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/src/Project.zig b/src/Project.zig index fb12bce..9ba122d 100644 --- a/src/Project.zig +++ b/src/Project.zig @@ -60,6 +60,7 @@ pub const StartLspError = (error{ ThespianSpawnFailed, Timeout, InvalidLspComman pub const LspError = (error{ NoLsp, LspFailed } || OutOfMemoryError || std.Io.Writer.Error); pub const ClientError = (error{ClientFailed} || OutOfMemoryError || std.Io.Writer.Error); pub const LspOrClientError = (LspError || ClientError); +pub const GitError = error{InvalidGitResponse}; const File = struct { path: []const u8, @@ -2538,3 +2539,72 @@ fn process_status(self: *Self, parent: tp.pid_ref, m: tp.message) (OutOfMemoryEr } } } + +pub fn request_vcs_id(self: *Self, file_path: []const u8) error{OutOfMemory}!void { + const request = try self.allocator.create(VcsIdRequest); + request.* = .{ + .allocator = self.allocator, + .project = self, + .file_path = try self.allocator.dupe(u8, file_path), + }; + git.rev_parse(@intFromPtr(request), "HEAD", file_path) catch |e| + self.logger_git.print_err("rev-parse", "failed: {t}", .{e}); +} + +pub const VcsIdRequest = struct { + allocator: std.mem.Allocator, + project: *Self, + file_path: []const u8, + + pub fn deinit(self: *@This()) void { + self.allocator.free(self.file_path); + self.allocator.destroy(self); + } +}; + +pub fn request_vcs_content(self: *Self, file_path: []const u8, vcs_id: []const u8) error{OutOfMemory}!void { + const request = try self.allocator.create(VcsContentRequest); + request.* = .{ + .allocator = self.allocator, + .project = self, + .file_path = try self.allocator.dupe(u8, file_path), + .vcs_id = try self.allocator.dupe(u8, vcs_id), + }; + self.logger_git.print("cat-file request {}:{s}:{s}", .{ request, vcs_id, file_path }); + git.cat_file(@intFromPtr(request), vcs_id) catch |e| + self.logger_git.print_err("cat-file", "failed: {t}", .{e}); +} + +pub const VcsContentRequest = struct { + allocator: std.mem.Allocator, + project: *Self, + file_path: []const u8, + vcs_id: []const u8, + + pub fn deinit(self: *@This()) void { + self.allocator.free(self.vcs_id); + self.allocator.free(self.file_path); + self.allocator.destroy(self); + } +}; + +pub fn process_git_response(self: *Self, parent: tp.pid_ref, m: tp.message) (OutOfMemoryError || GitError || error{Exit})!void { + var context: usize = undefined; + var vcs_id: []const u8 = undefined; + var vcs_content: []const u8 = undefined; + _ = self; + + if (try m.match(.{ tp.any, tp.extract(&context), "rev_parse", tp.extract(&vcs_id) })) { + const request: *VcsIdRequest = @ptrFromInt(context); + parent.send(.{ "PRJ", "vcs_id", request.file_path, vcs_id }) catch {}; + } else if (try m.match(.{ tp.any, tp.extract(&context), "rev_parse", tp.null_ })) { + const request: *VcsIdRequest = @ptrFromInt(context); + defer request.deinit(); + } else if (try m.match(.{ tp.any, tp.extract(&context), "cat_file", tp.extract(&vcs_content) })) { + const request: *VcsContentRequest = @ptrFromInt(context); + parent.send(.{ "PRJ", "vcs_content", request.file_path, request.vcs_id, vcs_content }) catch {}; + } else if (try m.match(.{ tp.any, tp.extract(&context), "cat_file", tp.null_ })) { + const request: *VcsContentRequest = @ptrFromInt(context); + defer request.deinit(); + } +} diff --git a/src/project_manager.zig b/src/project_manager.zig index 94703d8..d889aa2 100644 --- a/src/project_manager.zig +++ b/src/project_manager.zig @@ -172,6 +172,20 @@ pub fn request_vcs_status() (ProjectManagerError || ProjectError)!void { return send(.{ "request_vcs_status", project }); } +pub fn request_vcs_id(file_path: []const u8) (ProjectManagerError || ProjectError)!void { + const project = tp.env.get().str("project"); + if (project.len == 0) + return error.NoProject; + return send(.{ "request_vcs_id", project, file_path }); +} + +pub fn request_vcs_content(file_path: []const u8, vcs_id: []const u8) (ProjectManagerError || ProjectError)!void { + const project = tp.env.get().str("project"); + if (project.len == 0) + return error.NoProject; + return send(.{ "request_vcs_content", project, file_path, vcs_id }); +} + pub fn add_task(task: []const u8) (ProjectManagerError || ProjectError)!void { const project = tp.env.get().str("project"); if (project.len == 0) @@ -398,6 +412,7 @@ const Process = struct { var context: usize = undefined; var tag: []const u8 = undefined; var message: []const u8 = undefined; + var vcs_id: []const u8 = undefined; var eol_mode: Buffer.EolModeTag = @intFromEnum(Buffer.EolMode.lf); @@ -407,6 +422,12 @@ const Process = struct { } else if (try cbor.match(m.buf, .{ "walk_tree_done", tp.extract(&project_directory) })) { if (self.projects.get(project_directory)) |project| project.walk_tree_done(self.parent.ref()) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed; + } else if (try cbor.match(m.buf, .{ "git", tp.extract(&context), "rev_parse", tp.more })) { + const request: *Project.VcsIdRequest = @ptrFromInt(context); + request.project.process_git_response(self.parent.ref(), m) catch |e| self.logger.err("git-rev-parse", e); + } else if (try cbor.match(m.buf, .{ "git", tp.extract(&context), "cat_file", tp.more })) { + const request: *Project.VcsContentRequest = @ptrFromInt(context); + request.project.process_git_response(self.parent.ref(), m) catch |e| self.logger.err("git-cat-file", e); } else if (try cbor.match(m.buf, .{ "git", tp.extract(&context), tp.more })) { const project: *Project = @ptrFromInt(context); project.process_git(self.parent.ref(), m) catch {}; @@ -444,6 +465,10 @@ const Process = struct { self.request_tasks(from, project_directory) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed; } else if (try cbor.match(m.buf, .{ "request_vcs_status", tp.extract(&project_directory) })) { self.request_vcs_status(from, project_directory) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed; + } else if (try cbor.match(m.buf, .{ "request_vcs_id", tp.extract(&project_directory), tp.extract(&path) })) { + self.request_vcs_id(from, project_directory, path) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed; + } else if (try cbor.match(m.buf, .{ "request_vcs_content", tp.extract(&project_directory), tp.extract(&path), tp.extract(&vcs_id) })) { + self.request_vcs_content(from, project_directory, path, vcs_id) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed; } else if (try cbor.match(m.buf, .{ "add_task", tp.extract(&project_directory), tp.extract(&task) })) { self.add_task(project_directory, task) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed; } else if (try cbor.match(m.buf, .{ "delete_task", tp.extract(&project_directory), tp.extract(&task) })) { @@ -639,6 +664,16 @@ const Process = struct { try project.request_vcs_status(from); } + fn request_vcs_id(self: *Process, _: tp.pid_ref, project_directory: []const u8, file_path: []const u8) (ProjectError || Project.ClientError)!void { + const project = self.projects.get(project_directory) orelse return error.NoProject; + try project.request_vcs_id(file_path); + } + + fn request_vcs_content(self: *Process, _: tp.pid_ref, project_directory: []const u8, file_path: []const u8, vcs_id: []const u8) (ProjectError || Project.ClientError)!void { + const project = self.projects.get(project_directory) orelse return error.NoProject; + try project.request_vcs_content(file_path, vcs_id); + } + fn did_open(self: *Process, project_directory: []const u8, file_path: []const u8, file_type: []const u8, language_server: []const u8, language_server_options: []const u8, version: usize, text: []const u8) (ProjectError || Project.StartLspError || CallError || cbor.Error)!void { const frame = tracy.initZone(@src(), .{ .name = module_name ++ ".did_open" }); defer frame.deinit();