feat(tasks): stream task output to buffer in the background
This commit is contained in:
parent
649d369a56
commit
a28f1db4c7
2 changed files with 59 additions and 21 deletions
|
@ -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 BufferedWriter = std.io.BufferedWriter(max_chunk_size, Writer);
|
||||||
pub const Error = error{ InvalidShellArg0, OutOfMemory, Exit, ThespianSpawnFailed, Closed };
|
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 OutputHandler = fn (context: usize, 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 ExitHandler = fn (context: usize, parent: tp.pid_ref, arg0: []const u8, err_msg: []const u8, exit_code: i64) void;
|
||||||
|
|
||||||
pub const Handlers = struct {
|
pub const Handlers = struct {
|
||||||
|
context: usize = 0,
|
||||||
out: *const OutputHandler,
|
out: *const OutputHandler,
|
||||||
err: ?*const OutputHandler = null,
|
err: ?*const OutputHandler = null,
|
||||||
exit: *const ExitHandler = log_exit_handler,
|
exit: *const ExitHandler = log_exit_handler,
|
||||||
|
@ -74,7 +75,8 @@ pub fn bufferedWriter(self: *Self) BufferedWriter {
|
||||||
return .{ .unbuffered_writer = self.writer() };
|
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;
|
_ = parent;
|
||||||
_ = arg0;
|
_ = arg0;
|
||||||
const logger = log.logger(@typeName(Self));
|
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});
|
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;
|
_ = parent;
|
||||||
const logger = log.logger(@typeName(Self));
|
const logger = log.logger(@typeName(Self));
|
||||||
var it = std.mem.splitScalar(u8, output, '\n');
|
var it = std.mem.splitScalar(u8, output, '\n');
|
||||||
while (it.next()) |line| logger.print_err(arg0, "{s}", .{line});
|
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;
|
_ = parent;
|
||||||
const logger = log.logger(@typeName(Self));
|
const logger = log.logger(@typeName(Self));
|
||||||
if (exit_code > 0) {
|
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;
|
_ = parent;
|
||||||
const logger = log.logger(@typeName(Self));
|
const logger = log.logger(@typeName(Self));
|
||||||
if (exit_code > 0) {
|
if (exit_code > 0) {
|
||||||
|
@ -178,9 +183,9 @@ const Process = struct {
|
||||||
} else if (try m.match(.{"close"})) {
|
} else if (try m.match(.{"close"})) {
|
||||||
try self.close();
|
try self.close();
|
||||||
} else if (try m.match(.{ module_name, "stdout", tp.extract(&bytes) })) {
|
} 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) })) {
|
} 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 })) {
|
} else if (try m.match(.{ module_name, "term", tp.more })) {
|
||||||
self.handle_terminated(m) catch |e| return tp.exit_error(e, @errorReturnTrace());
|
self.handle_terminated(m) catch |e| return tp.exit_error(e, @errorReturnTrace());
|
||||||
} else if (try m.match(.{ "exit", "normal" })) {
|
} else if (try m.match(.{ "exit", "normal" })) {
|
||||||
|
@ -195,11 +200,11 @@ const Process = struct {
|
||||||
var err_msg: []const u8 = undefined;
|
var err_msg: []const u8 = undefined;
|
||||||
var exit_code: i64 = undefined;
|
var exit_code: i64 = undefined;
|
||||||
if (try m.match(.{ tp.any, tp.any, "exited", 0 })) {
|
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 })) {
|
} else if (try m.match(.{ tp.any, tp.any, "error.FileNotFound", 1 })) {
|
||||||
self.logger.print_err(self.arg0, "'{s}' executable not found", .{self.arg0});
|
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) })) {
|
} 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,7 +14,7 @@ const build_options = @import("build_options");
|
||||||
const Plane = @import("renderer").Plane;
|
const Plane = @import("renderer").Plane;
|
||||||
const input = @import("input");
|
const input = @import("input");
|
||||||
const command = @import("command");
|
const command = @import("command");
|
||||||
const BufferManager = @import("Buffer").Manager;
|
const Buffer = @import("Buffer");
|
||||||
|
|
||||||
const tui = @import("tui.zig");
|
const tui = @import("tui.zig");
|
||||||
const Box = @import("Box.zig");
|
const Box = @import("Box.zig");
|
||||||
|
@ -48,7 +48,7 @@ active_view: ?usize = 0,
|
||||||
panels: ?*WidgetList = null,
|
panels: ?*WidgetList = null,
|
||||||
last_match_text: ?[]const u8 = null,
|
last_match_text: ?[]const u8 = null,
|
||||||
location_history: location_history,
|
location_history: location_history,
|
||||||
buffer_manager: BufferManager,
|
buffer_manager: Buffer.Manager,
|
||||||
find_in_files_state: enum { init, adding, done } = .done,
|
find_in_files_state: enum { init, adding, done } = .done,
|
||||||
file_list_type: FileListType = .find_in_files,
|
file_list_type: FileListType = .find_in_files,
|
||||||
panel_height: ?usize = null,
|
panel_height: ?usize = null,
|
||||||
|
@ -70,7 +70,7 @@ pub fn create(allocator: std.mem.Allocator) !Widget {
|
||||||
.location_history = try location_history.create(),
|
.location_history = try location_history.create(),
|
||||||
.views = undefined,
|
.views = undefined,
|
||||||
.views_widget = undefined,
|
.views_widget = undefined,
|
||||||
.buffer_manager = BufferManager.init(allocator),
|
.buffer_manager = Buffer.Manager.init(allocator),
|
||||||
};
|
};
|
||||||
try self.commands.init(self);
|
try self.commands.init(self);
|
||||||
const w = Widget.to(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))
|
if (self.file_list_type == .diagnostics and self.is_panel_view_showing(filelist_view))
|
||||||
try self.toggle_panel_view(filelist_view, false);
|
try self.toggle_panel_view(filelist_view, false);
|
||||||
self.buffer_manager.deinit();
|
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);
|
try project_manager.open(project_dir);
|
||||||
const project = tp.env.get().str("project");
|
const project = tp.env.get().str("project");
|
||||||
tui.rdr().set_terminal_working_directory(project);
|
tui.rdr().set_terminal_working_directory(project);
|
||||||
|
@ -685,7 +685,7 @@ const cmds = struct {
|
||||||
return error.InvalidShellArgument;
|
return error.InvalidShellArgument;
|
||||||
const cmd = ctx.args;
|
const cmd = ctx.args;
|
||||||
const handlers = struct {
|
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 pos: usize = 0;
|
||||||
var nl_count: usize = 0;
|
var nl_count: usize = 0;
|
||||||
while (std.mem.indexOfScalarPos(u8, output, pos, '\n')) |next| {
|
while (std.mem.indexOfScalarPos(u8, output, pos, '\n')) |next| {
|
||||||
|
@ -711,10 +711,10 @@ const cmds = struct {
|
||||||
return error.InvalidShellArgument;
|
return error.InvalidShellArgument;
|
||||||
const cmd = ctx.args;
|
const cmd = ctx.args;
|
||||||
const handlers = struct {
|
const handlers = struct {
|
||||||
fn out(parent: tp.pid_ref, _: []const u8, output: []const u8) void {
|
fn out(buffer_ref: usize, parent: tp.pid_ref, _: []const u8, output: []const u8) void {
|
||||||
parent.send(.{ "cmd", "insert_chars", .{output} }) catch {};
|
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 buf: [256]u8 = undefined;
|
||||||
var stream = std.io.fixedBufferStream(&buf);
|
var stream = std.io.fixedBufferStream(&buf);
|
||||||
const writer = stream.writer();
|
const writer = stream.writer();
|
||||||
|
@ -723,14 +723,47 @@ const cmds = struct {
|
||||||
} else {
|
} else {
|
||||||
writer.print("\n'{s}' exited\n", .{arg0}) catch {};
|
writer.print("\n'{s}' exited\n", .{arg0}) catch {};
|
||||||
}
|
}
|
||||||
parent.send(.{ "cmd", "move_buffer_end", .{} }) catch {};
|
parent.send(.{ "cmd", "shell_execute_stream_output", .{ buffer_ref, stream.getWritten() } }) catch {};
|
||||||
parent.send(.{ "cmd", "insert_chars", .{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 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 {
|
pub fn adjust_fontsize(_: *Self, ctx: Ctx) Result {
|
||||||
var amount: f32 = undefined;
|
var amount: f32 = undefined;
|
||||||
if (!try ctx.args.match(.{tp.extract(&amount)}))
|
if (!try ctx.args.match(.{tp.extract(&amount)}))
|
||||||
|
|
Loading…
Add table
Reference in a new issue