Sfoglia il codice sorgente

Start working on the incremental output

This compiles, but _no_ idea at all if it runs if it were used.  Maybe
tomorrow.  🌘
Luna Stadler 4 anni fa
parent
commit
0cd18793e5
1 ha cambiato i file con 156 aggiunte e 0 eliminazioni
  1. 156 0
      zig/sdl/hello_sdl.zig

+ 156 - 0
zig/sdl/hello_sdl.zig

@ -16,6 +16,162 @@ const c = @cImport({
16 16
});
17 17
const std = @import("std");
18 18
19
// TODO: incremental output (e.g. for ag)
20
// TODO: instant output (for some commands like `py ...`, `go ...`, qcalc)
21
22
// commands wishlist:
23
// - search (e.g. default current dir + /usr/include)
24
// - launch with logs (default launcher, use systemd-run --user --unit=name name?)
25
// - view (the above logs)
26
// - switch to window
27
// - open url
28
// - open shortcuts (logs -> ..., tickets)
29
// - history (could be another command + some special keybindings)
30
31
// output line-by-line -> saved by caller?
32
// output can be reset
33
// incremental output vs. final output/action
34
35
const ProcessWithOutput = struct {
36
    process: std.ChildProcess,
37
    stdout: *std.ArrayList(u8),
38
    stderr: *std.ArrayList(u8),
39
40
    dead_fds: usize,
41
    max_output_bytes: usize,
42
43
    fn spawn(allocator: std.Allocator, argv: [][]u8, max_output_bytes: usize) ProcessWithOutput {
44
        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;
48
        try child.spawn();
49
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
    }
52
53
    fn is_running(self: ProcessWithOutput) bool {
54
        if (self.process.term) |_| {
55
            return false;
56
        } else {
57
            return true;
58
        }
59
    }
60
61
    fn stdout(self: ProcessWithOutput) []u8 {
62
        return self.stdout.allocatedSlice();
63
    }
64
65
    fn stderr(self: ProcessWithOutput) []u8 {
66
        return self.stdout.allocatedSlice();
67
    }
68
69
    // poll: https://github.com/ziglang/zig/blob/master/lib/std/child_process.zig#L206
70
    //   basically do one iteration with no blocking each time it runs and thus get the output incrementally?
71
    fn poll(self: ProcessWithOutput) !void {
72
        if (!self.is_running()) {
73
            return;
74
        }
75
76
        var poll_fds = [_]std.os.pollfd{
77
            .{ .fd = self.process.stdout.?.handle, .events = std.os.POLL.IN, .revents = undefined },
78
            .{ .fd = self.process.stderr.?.handle, .events = std.os.POLL.IN, .revents = undefined },
79
        };
80
81
        // We ask for ensureTotalCapacity with this much extra space. This has more of an
82
        // effect on small reads because once the reads start to get larger the amount
83
        // of space an ArrayList will allocate grows exponentially.
84
        const bump_amt = 512;
85
86
        const err_mask = std.os.POLL.ERR | std.os.POLL.NVAL | std.os.POLL.HUP;
87
88
        if (self.dead_fds >= poll_fds.len) {
89
            return;
90
        }
91
92
        const events = try std.os.poll(&poll_fds, 0);
93
        if (events == 0) {
94
            return;
95
        }
96
97
        var remove_stdout = false;
98
        var remove_stderr = false;
99
        // Try reading whatever is available before checking the error
100
        // conditions.
101
        // It's still pstd.ossible to read after a POLL.HUP is received, always
102
        // check if there's some data waiting to be read first.
103
        if (poll_fds[0].revents & std.os.POLL.IN != 0) {
104
            // 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();
108
            if (buf.len == 0) return error.StdoutStreamTooLong;
109
            const nread = try std.os.read(poll_fds[0].fd, buf);
110
            self.stdout.items.len += nread;
111
112
            // Remove the fd when the EOF condition is met.
113
            remove_stdout = nread == 0;
114
        } else {
115
            remove_stdout = poll_fds[0].revents & err_mask != 0;
116
        }
117
118
        if (poll_fds[1].revents & std.os.POLL.IN != 0) {
119
            // 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();
123
            if (buf.len == 0) return error.StderrStreamTooLong;
124
            const nread = try std.os.read(poll_fds[1].fd, buf);
125
            self.stderr.items.len += nread;
126
127
            // Remove the fd when the EOF condition is met.
128
            remove_stderr = nread == 0;
129
        } else {
130
            remove_stderr = poll_fds[1].revents & err_mask != 0;
131
        }
132
133
        // Exclude the fds that signaled an error.
134
        if (remove_stdout) {
135
            poll_fds[0].fd = -1;
136
            self.dead_fds += 1;
137
        }
138
        if (remove_stderr) {
139
            poll_fds[1].fd = -1;
140
            self.dead_fds += 1;
141
        }
142
    }
143
};
144
145
const RegexRunner = struct {
146
    run_always: bool,
147
    process: ProcessWithOutput,
148
149
    command_to_argv: fn (cmd: []u8) [][]u8,
150
151
    fn run(self: RegexRunner, cmd: []u8, is_confirmed: true) !void {
152
        if (!self.run_always and !is_confirmed) {
153
            return;
154
        }
155
156
        if (self.is_running()) {
157
            self.process.kill();
158
        }
159
        const argv = self.command_to_argv(cmd);
160
        self.process = ProcessWithOutput(argv);
161
    }
162
163
    fn output(self: RegexRunner) []u8 {
164
        if (!self.is_running()) {
165
            // TODO: return buffer
166
            return "<done>";
167
        }
168
169
        // get more input?
170
171
        // TODO: return buffer
172
    }
173
};
174
19 175
pub fn main() !void {
20 176
    var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
21 177
    defer {