Просмотр исходного кода

Make some commands work again, but incrementally!?

This is pretty neat now, and involves some very odd tricks.
Luna Stadler лет назад: 4
Родитель
Сommit
5bf136f8a6
1 измененных файлов с 147 добавлено и 55 удалено
  1. 147 55
      zig/sdl/hello_sdl.zig

+ 147 - 55
zig/sdl/hello_sdl.zig

@ -16,6 +16,7 @@ const c = @cImport({
16 16
});
17 17
const std = @import("std");
18 18
19
// TODO: restore commands
19 20
// TODO: instant output (for some commands like `py ...`, `go ...`, qcalc)
20 21
21 22
// commands wishlist:
@ -43,6 +44,7 @@ const ProcessWithOutput = struct {
43 44
44 45
    fn spawn(allocator: *std.mem.Allocator, argv: []const []const u8, max_output_bytes: usize) !ProcessWithOutput {
45 46
        const child = try std.ChildProcess.init(argv, allocator);
47
        child.expand_arg0 = std.ChildProcess.Arg0Expand.expand;
46 48
        child.stdin_behavior = std.ChildProcess.StdIo.Ignore;
47 49
        child.stdout_behavior = std.ChildProcess.StdIo.Pipe;
48 50
        child.stderr_behavior = std.ChildProcess.StdIo.Pipe;
@ -59,11 +61,11 @@ const ProcessWithOutput = struct {
59 61
        }
60 62
    }
61 63
62
    fn stdout(self: ProcessWithOutput) []u8 {
64
    fn stdout(self: *ProcessWithOutput) []u8 {
63 65
        return self.stdout_buf.items;
64 66
    }
65 67
66
    fn stderr(self: ProcessWithOutput) []u8 {
68
    fn stderr(self: *ProcessWithOutput) []u8 {
67 69
        return self.stderr_buf.items;
68 70
    }
69 71
@ -151,31 +153,109 @@ const ProcessWithOutput = struct {
151 153
152 154
const RegexRunner = struct {
153 155
    run_always: bool,
154
    process: ProcessWithOutput,
156
    process: ?ProcessWithOutput = null,
155 157
156
    command_to_argv: fn (cmd: []u8) [][]u8,
158
    toArgv: fn (cmd: []const u8) []const []const u8,
159
    isActive: fn (cmd: []const u8) bool,
157 160
158
    fn run(self: RegexRunner, cmd: []u8, is_confirmed: true) !void {
161
    fn run(self: *RegexRunner, allocator: *std.mem.Allocator, cmd: []const u8, is_confirmed: bool) !bool {
159 162
        if (!self.run_always and !is_confirmed) {
160
            return;
163
            return false;
164
        }
165
166
        if (!self.isActive(cmd)) {
167
            return false;
161 168
        }
162 169
163
        if (self.is_running()) {
164
            self.process.kill();
170
        // stop already running command, restart with new cmd
171
        if (self.process) |*process| {
172
            if (process.is_running()) {
173
                _ = try process.process.kill();
174
                process.deinit();
175
            }
165 176
        }
166
        const argv = self.command_to_argv(cmd);
167
        self.process = ProcessWithOutput(argv);
177
178
        const argv = self.toArgv(cmd);
179
        std.debug.print("{s} -> {s}\n", .{ cmd, argv });
180
        self.process = try ProcessWithOutput.spawn(allocator, argv, 1024 * 1024);
181
182
        return true;
168 183
    }
169 184
170
    fn output(self: RegexRunner) []u8 {
171
        if (!self.is_running()) {
172
            // TODO: return buffer
173
            return "<done>";
185
    fn output(self: *RegexRunner) ![]const u8 {
186
        if (self.process) |*process| {
187
            process.poll() catch |err| switch (err) {
188
                error.StdoutStreamTooLong => {
189
                    std.debug.print("too much output, killing\n", .{});
190
                    _ = try process.process.kill();
191
                },
192
                else => {
193
                    return err;
194
                },
195
            };
196
            //std.debug.print("{d} ({d})\n", .{process.stdout_buf.items.len, process.stderr_buf.items.len});
197
            if (process.stdout_buf.items.len > 0) {
198
                return process.stdout();
199
            } else if (process.stderr_buf.items.len > 0) {
200
                return process.stderr();
201
            }
174 202
        }
175 203
176
        // get more input?
204
        return "<no output>";
205
    }
206
207
    fn deinit(self: *RegexRunner) void {
208
        if (self.process) |*process| {
209
            process.deinit();
210
        }
211
    }
212
};
213
214
var cmd_buf: [100]u8 = undefined;
215
216
const GoDocRunner = struct {
217
    fn init() RegexRunner {
218
        return RegexRunner{ .run_always = true, .toArgv = toArgv, .isActive = isActive };
219
    }
220
221
    fn isActive(cmd: []const u8) bool {
222
        return cmd.len > 3 and std.mem.startsWith(u8, cmd, "go ");
223
    }
224
225
    fn toArgv(cmd: []const u8) []const []const u8 {
226
        // NO idea why bufPrint is required, but without `cmd` will just be some random bit of memory, which is rude.
227
        _ = std.fmt.bufPrint(&cmd_buf, "{s}", .{cmd["go ".len..]}) catch "???";
228
        return &[_][]const u8{ "go", "doc", &cmd_buf };
229
    }
230
};
231
232
const PythonHelpRunner = struct {
233
    fn init() RegexRunner {
234
        return RegexRunner{ .run_always = true, .toArgv = toArgv, .isActive = isActive };
235
    }
236
237
    fn isActive(cmd: []const u8) bool {
238
        return cmd.len > 3 and std.mem.startsWith(u8, cmd, "py ");
239
    }
240
241
    fn toArgv(cmd: []const u8) []const []const u8 {
242
        _ = std.fmt.bufPrint(&cmd_buf, "import {s}; help({s});", .{ std.mem.sliceTo(cmd["py ".len..], '.'), cmd["py ".len..] }) catch "???";
243
        return &[_][]const u8{ "python", "-c", &cmd_buf };
244
    }
245
};
246
247
const SearchRunner = struct {
248
    fn init() RegexRunner {
249
        return RegexRunner{ .run_always = true, .toArgv = toArgv, .isActive = isActive };
250
    }
251
252
    fn isActive(cmd: []const u8) bool {
253
        return cmd.len > "s ".len and std.mem.startsWith(u8, cmd, "s ");
254
    }
177 255
178
        // TODO: return buffer
256
    fn toArgv(cmd: []const u8) []const []const u8 {
257
        _ = std.fmt.bufPrint(&cmd_buf, "{s}", .{cmd["s ".len..]}) catch "???";
258
        return &[_][]const u8{ "ag", &cmd_buf, "/home/luna/k/the-thing" };
179 259
    }
180 260
};
181 261
@ -244,14 +324,19 @@ pub fn main() !void {
244 324
    const keyboardState = c.SDL_GetKeyboardState(null);
245 325
246 326
    c.SDL_StartTextInput();
247
248
    var process = &try ProcessWithOutput.spawn(gpa, &[_][]const u8{ "ag", "\\bshit\\b", "/usr/include" }, 1024 * 1024);
327
    var commands = [_]RegexRunner{
328
        GoDocRunner.init(),
329
        PythonHelpRunner.init(),
330
        SearchRunner.init(),
331
    };
249 332
250 333
    var quit = false;
251 334
    var skip: i32 = 0;
252 335
    var num_lines: i32 = 0;
253 336
    while (!quit) {
254 337
        var event: c.SDL_Event = undefined;
338
        var changed = false;
339
        var confirmed = false;
255 340
        while (c.SDL_PollEvent(&event) != 0) {
256 341
            const ctrlPressed = (keyboardState[c.SDL_SCANCODE_LCTRL] != 0);
257 342
            switch (event.@"type") {
@ -302,6 +387,8 @@ pub fn main() !void {
302 387
                                    msg[max_chars] = 0;
303 388
                                }
304 389
                                c.SDL_free(clipboard_text);
390
391
                                changed = true;
305 392
                            },
306 393
                            else => {},
307 394
                        }
@ -313,24 +400,12 @@ pub fn main() !void {
313 400
                            c.SDLK_BACKSPACE => {
314 401
                                pos = if (pos == 0) max_chars - 1 else (pos - 1) % (max_chars - 1);
315 402
                                msg[pos] = '_';
403
                                changed = true;
316 404
                            },
317 405
                            c.SDLK_RETURN => {
318 406
                                skip = 0;
319
                                gpa.free(result);
320
                                result = try runCommand(&msg, gpa);
321
                                var i: usize = 0;
322
                                while (i < max_chars) : (i += 1) {
323
                                    msg[i] = ' ';
324
                                }
325
                                msg[max_chars] = 0;
326
                                pos = 0;
327 407
328
                                num_lines = 0;
329
                                var lines = std.mem.split(u8, result, "\n");
330
                                var line = lines.next();
331
                                while (line != null) : (line = lines.next()) {
332
                                    num_lines += 1;
333
                                }
408
                                confirmed = true;
334 409
                            },
335 410
                            c.SDLK_UP => {
336 411
                                if (skip > 0) {
@ -367,12 +442,22 @@ pub fn main() !void {
367 442
                        c.SDL_Log("input: '%s' at %d", event.text.text, pos);
368 443
                        msg[pos] = event.text.text[0];
369 444
                        pos = (pos + 1) % (max_chars - 1);
445
446
                        changed = true;
370 447
                    }
371 448
                },
372 449
                else => {},
373 450
            }
374 451
        }
375 452
453
        const cmd = std.mem.trim(u8, std.mem.sliceTo(&msg, 0), &std.ascii.spaces);
454
455
        if (changed) {
456
            for (commands) |*command| {
457
                _ = try command.run(gpa, cmd, confirmed);
458
            }
459
        }
460
376 461
        _ = c.SDL_SetRenderDrawColor(renderer, 0, 0, 0, 100);
377 462
        //_ = c.SDL_SetRenderDrawBlendMode(renderer, c.SDL_BlendMode.SDL_BLENDMODE_BLEND);
378 463
        _ = c.SDL_RenderClear(renderer);
@ -388,37 +473,44 @@ pub fn main() !void {
388 473
        _ = c.SDL_RenderCopy(renderer, texture, null, &c.SDL_Rect{ .x = 0, .y = 0, .w = @intCast(c_int, msg.len) * glyph_width, .h = glyph_height });
389 474
390 475
        var i: c_int = 1;
391
        var lines = std.mem.split(u8, result, "\n");
392
        var line = lines.next();
393
        {
394
            var skipped: i32 = 0;
395
            while (skipped < skip and line != null) : (skipped += 1) {
396
                line = lines.next();
476
        for (commands) |*command| {
477
            if (!command.isActive(cmd)) {
478
                continue;
397 479
            }
398
        }
399 480
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();
404
        while (line != null and i * glyph_height < window_height) {
405
            const line_c = try gpa.dupeZ(u8, line.?);
406
            // TODO: render tabs at correct width (or some width at least)
407
            const result_text = c.TTF_RenderUTF8_Shaded(font, line_c, white, black);
408
            gpa.free(line_c);
409
            const result_texture = c.SDL_CreateTextureFromSurface(renderer, result_text);
410
            _ = c.SDL_RenderCopy(renderer, result_texture, null, &c.SDL_Rect{ .x = 0, .y = i * glyph_height, .w = @intCast(c_int, line.?.len) * glyph_width, .h = glyph_height });
411
            c.SDL_FreeSurface(result_text);
412
            c.SDL_DestroyTexture(result_texture);
413
414
            i += 1;
415
            line = lines.next();
481
            //std.debug.print("{s} {d} {d}\n", .{ command.process.is_running(), command.process.stdout_buf.items.len, command.process.stdout_buf.capacity });
482
            var lines = std.mem.split(u8, try command.output(), "\n");
483
            var line = lines.next();
484
            {
485
                var skipped: i32 = 0;
486
                while (skipped < skip and line != null) : (skipped += 1) {
487
                    line = lines.next();
488
                }
489
            }
490
            while (line != null and i * glyph_height < window_height) {
491
                const line_c = try gpa.dupeZ(u8, line.?);
492
                // TODO: render tabs at correct width (or some width at least)
493
                const result_text = c.TTF_RenderUTF8_Shaded(font, line_c, white, black);
494
                gpa.free(line_c);
495
                const result_texture = c.SDL_CreateTextureFromSurface(renderer, result_text);
496
                _ = c.SDL_RenderCopy(renderer, result_texture, null, &c.SDL_Rect{ .x = 0, .y = i * glyph_height, .w = @intCast(c_int, line.?.len) * glyph_width, .h = glyph_height });
497
                c.SDL_FreeSurface(result_text);
498
                c.SDL_DestroyTexture(result_texture);
499
500
                i += 1;
501
                line = lines.next();
502
            }
416 503
        }
417 504
418 505
        _ = c.SDL_RenderPresent(renderer);
419 506
420 507
        c.SDL_Delay(16);
421 508
    }
509
510
    // clean up memory and processes
511
    for (commands) |*command| {
512
        command.deinit();
513
    }
422 514
}
423 515
424 516
fn runCommand(raw_cmd: []const u8, allocator: *std.mem.Allocator) ![]const u8 {