|
|
@ -16,7 +16,6 @@ const c = @cImport({
|
|
16
|
16
|
});
|
|
17
|
17
|
const std = @import("std");
|
|
18
|
18
|
|
|
19
|
|
// TODO: incremental output (e.g. for ag)
|
|
20
|
19
|
// TODO: instant output (for some commands like `py ...`, `go ...`, qcalc)
|
|
21
|
20
|
|
|
22
|
21
|
// commands wishlist:
|
|
|
@ -33,24 +32,26 @@ const std = @import("std");
|
|
33
|
32
|
// incremental output vs. final output/action
|
|
34
|
33
|
|
|
35
|
34
|
const ProcessWithOutput = struct {
|
|
36
|
|
process: std.ChildProcess,
|
|
37
|
|
stdout: *std.ArrayList(u8),
|
|
38
|
|
stderr: *std.ArrayList(u8),
|
|
|
35
|
process: *std.ChildProcess,
|
|
|
36
|
stdout_buf: std.ArrayList(u8),
|
|
|
37
|
stderr_buf: std.ArrayList(u8),
|
|
39
|
38
|
|
|
40
|
|
dead_fds: usize,
|
|
41
|
|
max_output_bytes: usize,
|
|
|
39
|
dead_fds: usize = 0,
|
|
|
40
|
max_output_bytes: usize = 50 * 1024,
|
|
42
|
41
|
|
|
43
|
|
fn spawn(allocator: std.Allocator, argv: [][]u8, max_output_bytes: usize) ProcessWithOutput {
|
|
|
42
|
cleanup_done: bool = false,
|
|
|
43
|
|
|
|
44
|
fn spawn(allocator: *std.mem.Allocator, argv: []const []const u8, max_output_bytes: usize) !ProcessWithOutput {
|
|
44
|
45
|
const child = try std.ChildProcess.init(argv, allocator);
|
|
45
|
|
child.stdin_behavior = std.ChildProcess.Ignore;
|
|
46
|
|
child.stdout_behavior = std.ChildProcess.Pipe;
|
|
47
|
|
child.stderr_behavior = std.ChildProcess.Pipe;
|
|
|
46
|
child.stdin_behavior = std.ChildProcess.StdIo.Ignore;
|
|
|
47
|
child.stdout_behavior = std.ChildProcess.StdIo.Pipe;
|
|
|
48
|
child.stderr_behavior = std.ChildProcess.StdIo.Pipe;
|
|
48
|
49
|
try child.spawn();
|
|
49
|
50
|
|
|
50
|
|
return ProcessWithOutput{ .process = child, .stdout = std.ArrayList(u8).init(allocator), .stderr = std.ArrayList(u8).init(allocator), .dead_fds = 0, .max_output_bytes = max_output_bytes };
|
|
|
51
|
return ProcessWithOutput{ .process = child, .stdout_buf = std.ArrayList(u8).init(allocator), .stderr_buf = std.ArrayList(u8).init(allocator), .dead_fds = 0, .max_output_bytes = max_output_bytes };
|
|
51
|
52
|
}
|
|
52
|
53
|
|
|
53
|
|
fn is_running(self: ProcessWithOutput) bool {
|
|
|
54
|
fn is_running(self: *ProcessWithOutput) bool {
|
|
54
|
55
|
if (self.process.term) |_| {
|
|
55
|
56
|
return false;
|
|
56
|
57
|
} else {
|
|
|
@ -59,16 +60,16 @@ const ProcessWithOutput = struct {
|
|
59
|
60
|
}
|
|
60
|
61
|
|
|
61
|
62
|
fn stdout(self: ProcessWithOutput) []u8 {
|
|
62
|
|
return self.stdout.allocatedSlice();
|
|
|
63
|
return self.stdout_buf.items;
|
|
63
|
64
|
}
|
|
64
|
65
|
|
|
65
|
66
|
fn stderr(self: ProcessWithOutput) []u8 {
|
|
66
|
|
return self.stdout.allocatedSlice();
|
|
|
67
|
return self.stderr_buf.items;
|
|
67
|
68
|
}
|
|
68
|
69
|
|
|
69
|
70
|
// poll: https://github.com/ziglang/zig/blob/master/lib/std/child_process.zig#L206
|
|
70
|
71
|
// basically do one iteration with no blocking each time it runs and thus get the output incrementally?
|
|
71
|
|
fn poll(self: ProcessWithOutput) !void {
|
|
|
72
|
fn poll(self: *ProcessWithOutput) !void {
|
|
72
|
73
|
if (!self.is_running()) {
|
|
73
|
74
|
return;
|
|
74
|
75
|
}
|
|
|
@ -102,12 +103,12 @@ const ProcessWithOutput = struct {
|
|
102
|
103
|
// check if there's some data waiting to be read first.
|
|
103
|
104
|
if (poll_fds[0].revents & std.os.POLL.IN != 0) {
|
|
104
|
105
|
// stdout is ready.
|
|
105
|
|
const new_capacity = std.math.min(self.stdout.items.len + bump_amt, self.max_output_bytes);
|
|
106
|
|
try self.stdout.ensureTotalCapacity(new_capacity);
|
|
107
|
|
const buf = self.stdout.unusedCapacitySlice();
|
|
|
106
|
const new_capacity = std.math.min(self.stdout_buf.items.len + bump_amt, self.max_output_bytes);
|
|
|
107
|
try self.stdout_buf.ensureTotalCapacity(new_capacity);
|
|
|
108
|
const buf = self.stdout_buf.unusedCapacitySlice();
|
|
108
|
109
|
if (buf.len == 0) return error.StdoutStreamTooLong;
|
|
109
|
110
|
const nread = try std.os.read(poll_fds[0].fd, buf);
|
|
110
|
|
self.stdout.items.len += nread;
|
|
|
111
|
self.stdout_buf.items.len += nread;
|
|
111
|
112
|
|
|
112
|
113
|
// Remove the fd when the EOF condition is met.
|
|
113
|
114
|
remove_stdout = nread == 0;
|
|
|
@ -117,12 +118,12 @@ const ProcessWithOutput = struct {
|
|
117
|
118
|
|
|
118
|
119
|
if (poll_fds[1].revents & std.os.POLL.IN != 0) {
|
|
119
|
120
|
// stderr is ready.
|
|
120
|
|
const new_capacity = std.math.min(self.stderr.items.len + bump_amt, self.max_output_bytes);
|
|
121
|
|
try self.stderr.ensureTotalCapacity(new_capacity);
|
|
122
|
|
const buf = self.stderr.unusedCapacitySlice();
|
|
|
121
|
const new_capacity = std.math.min(self.stderr_buf.items.len + bump_amt, self.max_output_bytes);
|
|
|
122
|
try self.stderr_buf.ensureTotalCapacity(new_capacity);
|
|
|
123
|
const buf = self.stderr_buf.unusedCapacitySlice();
|
|
123
|
124
|
if (buf.len == 0) return error.StderrStreamTooLong;
|
|
124
|
125
|
const nread = try std.os.read(poll_fds[1].fd, buf);
|
|
125
|
|
self.stderr.items.len += nread;
|
|
|
126
|
self.stderr_buf.items.len += nread;
|
|
126
|
127
|
|
|
127
|
128
|
// Remove the fd when the EOF condition is met.
|
|
128
|
129
|
remove_stderr = nread == 0;
|
|
|
@ -140,6 +141,12 @@ const ProcessWithOutput = struct {
|
|
140
|
141
|
self.dead_fds += 1;
|
|
141
|
142
|
}
|
|
142
|
143
|
}
|
|
|
144
|
|
|
|
145
|
fn deinit(self: *ProcessWithOutput) void {
|
|
|
146
|
self.stdout_buf.deinit();
|
|
|
147
|
self.stderr_buf.deinit();
|
|
|
148
|
self.process.deinit();
|
|
|
149
|
}
|
|
143
|
150
|
};
|
|
144
|
151
|
|
|
145
|
152
|
const RegexRunner = struct {
|
|
|
@ -238,6 +245,8 @@ pub fn main() !void {
|
|
238
|
245
|
|
|
239
|
246
|
c.SDL_StartTextInput();
|
|
240
|
247
|
|
|
|
248
|
var process = &try ProcessWithOutput.spawn(gpa, &[_][]const u8{ "ag", "\\bshit\\b", "/usr/include" }, 1024 * 1024);
|
|
|
249
|
|
|
241
|
250
|
var quit = false;
|
|
242
|
251
|
var skip: i32 = 0;
|
|
243
|
252
|
var num_lines: i32 = 0;
|
|
|
@ -387,6 +396,11 @@ pub fn main() !void {
|
|
387
|
396
|
line = lines.next();
|
|
388
|
397
|
}
|
|
389
|
398
|
}
|
|
|
399
|
|
|
|
400
|
try process.poll();
|
|
|
401
|
std.debug.print("{s} {d} {d}\n", .{ process.is_running(), process.stdout_buf.items.len, process.stdout_buf.capacity });
|
|
|
402
|
lines = std.mem.split(u8, process.stdout(), "\n");
|
|
|
403
|
line = lines.next();
|
|
390
|
404
|
while (line != null and i * glyph_height < window_height) {
|
|
391
|
405
|
const line_c = try gpa.dupeZ(u8, line.?);
|
|
392
|
406
|
// TODO: render tabs at correct width (or some width at least)
|