Przeglądaj źródła

Implement demo for streaming output (which breaks all commands)

Commands are broken/disabled on purpose, because we have to reimplement
them anyways and without this test case I wasn't getting anywhere.
Luna Stadler 4 lat temu
rodzic
commit
5e40d7b1d2
1 zmienionych plików z 37 dodań i 23 usunięć
  1. 37 23
      zig/sdl/hello_sdl.zig

+ 37 - 23
zig/sdl/hello_sdl.zig

@ -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)