From a28f1db4c77ca2ce777f2c865d8419a0430a3481 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Sun, 26 Jan 2025 20:17:58 +0100 Subject: [PATCH] feat(tasks): stream task output to buffer in the background --- src/shell.zig | 25 ++++++++++++-------- src/tui/mainview.zig | 55 +++++++++++++++++++++++++++++++++++--------- 2 files changed, 59 insertions(+), 21 deletions(-) diff --git a/src/shell.zig b/src/shell.zig index cdcde14..0816bfb 100644 --- a/src/shell.zig +++ b/src/shell.zig @@ -13,10 +13,11 @@ pub const Writer = std.io.Writer(*Self, Error, write); pub const BufferedWriter = std.io.BufferedWriter(max_chunk_size, Writer); pub const Error = error{ InvalidShellArg0, OutOfMemory, Exit, ThespianSpawnFailed, Closed }; -pub const OutputHandler = fn (parent: tp.pid_ref, arg0: []const u8, output: []const u8) void; -pub const ExitHandler = fn (parent: tp.pid_ref, arg0: []const u8, err_msg: []const u8, exit_code: i64) void; +pub const OutputHandler = fn (context: usize, parent: tp.pid_ref, arg0: []const u8, output: []const u8) void; +pub const ExitHandler = fn (context: usize, parent: tp.pid_ref, arg0: []const u8, err_msg: []const u8, exit_code: i64) void; pub const Handlers = struct { + context: usize = 0, out: *const OutputHandler, err: ?*const OutputHandler = null, exit: *const ExitHandler = log_exit_handler, @@ -74,7 +75,8 @@ pub fn bufferedWriter(self: *Self) BufferedWriter { return .{ .unbuffered_writer = self.writer() }; } -pub fn log_handler(parent: tp.pid_ref, arg0: []const u8, output: []const u8) void { +pub fn log_handler(context: usize, parent: tp.pid_ref, arg0: []const u8, output: []const u8) void { + _ = context; _ = parent; _ = arg0; const logger = log.logger(@typeName(Self)); @@ -82,14 +84,16 @@ pub fn log_handler(parent: tp.pid_ref, arg0: []const u8, output: []const u8) voi while (it.next()) |line| if (line.len > 0) logger.print("{s}", .{line}); } -pub fn log_err_handler(parent: tp.pid_ref, arg0: []const u8, output: []const u8) void { +pub fn log_err_handler(context: usize, parent: tp.pid_ref, arg0: []const u8, output: []const u8) void { + _ = context; _ = parent; const logger = log.logger(@typeName(Self)); var it = std.mem.splitScalar(u8, output, '\n'); while (it.next()) |line| logger.print_err(arg0, "{s}", .{line}); } -pub fn log_exit_handler(parent: tp.pid_ref, arg0: []const u8, err_msg: []const u8, exit_code: i64) void { +pub fn log_exit_handler(context: usize, parent: tp.pid_ref, arg0: []const u8, err_msg: []const u8, exit_code: i64) void { + _ = context; _ = parent; const logger = log.logger(@typeName(Self)); if (exit_code > 0) { @@ -99,7 +103,8 @@ pub fn log_exit_handler(parent: tp.pid_ref, arg0: []const u8, err_msg: []const u } } -pub fn log_exit_err_handler(parent: tp.pid_ref, arg0: []const u8, err_msg: []const u8, exit_code: i64) void { +pub fn log_exit_err_handler(context: usize, parent: tp.pid_ref, arg0: []const u8, err_msg: []const u8, exit_code: i64) void { + _ = context; _ = parent; const logger = log.logger(@typeName(Self)); if (exit_code > 0) { @@ -178,9 +183,9 @@ const Process = struct { } else if (try m.match(.{"close"})) { try self.close(); } else if (try m.match(.{ module_name, "stdout", tp.extract(&bytes) })) { - self.handlers.out(self.parent.ref(), self.arg0, bytes); + self.handlers.out(self.handlers.context, self.parent.ref(), self.arg0, bytes); } else if (try m.match(.{ module_name, "stderr", tp.extract(&bytes) })) { - (self.handlers.err orelse self.handlers.out)(self.parent.ref(), self.arg0, bytes); + (self.handlers.err orelse self.handlers.out)(self.handlers.context, self.parent.ref(), self.arg0, bytes); } else if (try m.match(.{ module_name, "term", tp.more })) { self.handle_terminated(m) catch |e| return tp.exit_error(e, @errorReturnTrace()); } else if (try m.match(.{ "exit", "normal" })) { @@ -195,11 +200,11 @@ const Process = struct { var err_msg: []const u8 = undefined; var exit_code: i64 = undefined; if (try m.match(.{ tp.any, tp.any, "exited", 0 })) { - self.handlers.exit(self.parent.ref(), self.arg0, "exited", 0); + self.handlers.exit(self.handlers.context, self.parent.ref(), self.arg0, "exited", 0); } else if (try m.match(.{ tp.any, tp.any, "error.FileNotFound", 1 })) { self.logger.print_err(self.arg0, "'{s}' executable not found", .{self.arg0}); } else if (try m.match(.{ tp.any, tp.any, tp.extract(&err_msg), tp.extract(&exit_code) })) { - self.handlers.exit(self.parent.ref(), self.arg0, err_msg, exit_code); + self.handlers.exit(self.handlers.context, self.parent.ref(), self.arg0, err_msg, exit_code); } } }; diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index 30c8fe2..24fe676 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -14,7 +14,7 @@ const build_options = @import("build_options"); const Plane = @import("renderer").Plane; const input = @import("input"); const command = @import("command"); -const BufferManager = @import("Buffer").Manager; +const Buffer = @import("Buffer"); const tui = @import("tui.zig"); const Box = @import("Box.zig"); @@ -48,7 +48,7 @@ active_view: ?usize = 0, panels: ?*WidgetList = null, last_match_text: ?[]const u8 = null, location_history: location_history, -buffer_manager: BufferManager, +buffer_manager: Buffer.Manager, find_in_files_state: enum { init, adding, done } = .done, file_list_type: FileListType = .find_in_files, panel_height: ?usize = null, @@ -70,7 +70,7 @@ pub fn create(allocator: std.mem.Allocator) !Widget { .location_history = try location_history.create(), .views = undefined, .views_widget = undefined, - .buffer_manager = BufferManager.init(allocator), + .buffer_manager = Buffer.Manager.init(allocator), }; try self.commands.init(self); const w = Widget.to(self); @@ -287,7 +287,7 @@ const cmds = struct { if (self.file_list_type == .diagnostics and self.is_panel_view_showing(filelist_view)) try self.toggle_panel_view(filelist_view, false); self.buffer_manager.deinit(); - self.buffer_manager = BufferManager.init(self.allocator); + self.buffer_manager = Buffer.Manager.init(self.allocator); try project_manager.open(project_dir); const project = tp.env.get().str("project"); tui.rdr().set_terminal_working_directory(project); @@ -685,7 +685,7 @@ const cmds = struct { return error.InvalidShellArgument; const cmd = ctx.args; const handlers = struct { - fn out(parent: tp.pid_ref, _: []const u8, output: []const u8) void { + fn out(_: usize, parent: tp.pid_ref, _: []const u8, output: []const u8) void { var pos: usize = 0; var nl_count: usize = 0; while (std.mem.indexOfScalarPos(u8, output, pos, '\n')) |next| { @@ -711,10 +711,10 @@ const cmds = struct { return error.InvalidShellArgument; const cmd = ctx.args; const handlers = struct { - fn out(parent: tp.pid_ref, _: []const u8, output: []const u8) void { - parent.send(.{ "cmd", "insert_chars", .{output} }) catch {}; + fn out(buffer_ref: usize, parent: tp.pid_ref, _: []const u8, output: []const u8) void { + parent.send(.{ "cmd", "shell_execute_stream_output", .{ buffer_ref, output } }) catch {}; } - fn exit(parent: tp.pid_ref, arg0: []const u8, err_msg: []const u8, exit_code: i64) void { + fn exit(buffer_ref: usize, parent: tp.pid_ref, arg0: []const u8, err_msg: []const u8, exit_code: i64) void { var buf: [256]u8 = undefined; var stream = std.io.fixedBufferStream(&buf); const writer = stream.writer(); @@ -723,14 +723,47 @@ const cmds = struct { } else { writer.print("\n'{s}' exited\n", .{arg0}) catch {}; } - parent.send(.{ "cmd", "move_buffer_end", .{} }) catch {}; - parent.send(.{ "cmd", "insert_chars", .{stream.getWritten()} }) catch {}; + parent.send(.{ "cmd", "shell_execute_stream_output", .{ buffer_ref, stream.getWritten() } }) catch {}; + parent.send(.{ "cmd", "shell_execute_stream_output_complete", .{buffer_ref} }) catch {}; } }; - try shell.execute(self.allocator, cmd, .{ .out = handlers.out, .err = handlers.out, .exit = handlers.exit }); + const editor = self.get_active_editor() orelse return error.Stop; + const buffer = editor.buffer orelse return error.Stop; + const buffer_ref = self.buffer_manager.buffer_to_ref(buffer); + try shell.execute(self.allocator, cmd, .{ .context = buffer_ref, .out = handlers.out, .err = handlers.out, .exit = handlers.exit }); } pub const shell_execute_stream_meta = .{ .arguments = &.{.string} }; + pub fn shell_execute_stream_output(self: *Self, ctx: Ctx) Result { + var buffer_ref: usize = 0; + var output: []const u8 = undefined; + if (!try ctx.args.match(.{ tp.extract(&buffer_ref), tp.extract(&output) })) + return error.InvalidShellOutputArgument; + const buffer = self.buffer_manager.buffer_from_ref(buffer_ref) orelse return; + if (self.get_active_editor()) |editor| if (editor.buffer) |eb| if (eb == buffer) { + editor.move_buffer_end(.{}) catch {}; + editor.insert_chars(command.fmt(.{output})) catch {}; + return; + }; + var cursor: Buffer.Cursor = .{}; + const metrics = self.plane.metrics(1); + cursor.move_buffer_end(buffer.root, metrics); + var root_ = buffer.root; + _, _, root_ = try root_.insert_chars(cursor.row, cursor.col, output, self.allocator, metrics); + buffer.store_undo(&[_]u8{}) catch {}; + buffer.update(root_); + } + pub const shell_execute_stream_output_meta = .{ .arguments = &.{ .integer, .string } }; + + pub fn shell_execute_stream_output_complete(self: *Self, ctx: Ctx) Result { + var buffer_ref: usize = 0; + if (!try ctx.args.match(.{tp.extract(&buffer_ref)})) + return error.InvalidShellOutputCompleteArgument; + // TODO + _ = self; + } + pub const shell_execute_stream_output_complete_meta = .{ .arguments = &.{ .integer, .string } }; + pub fn adjust_fontsize(_: *Self, ctx: Ctx) Result { var amount: f32 = undefined; if (!try ctx.args.match(.{tp.extract(&amount)}))