Sfoglia il codice sorgente

Make some commands work again, but incrementally!?

This is pretty neat now, and involves some very odd tricks.
Luna Stadler 4 anni fa
parent
commit
5bf136f8a6
1 ha cambiato i file con 147 aggiunte e 55 eliminazioni
  1. 147 55
      zig/sdl/hello_sdl.zig

+ 147 - 55
zig/sdl/hello_sdl.zig

16
});
16
});
17
const std = @import("std");
17
const std = @import("std");
18
18
19
// TODO: restore commands
19
// TODO: instant output (for some commands like `py ...`, `go ...`, qcalc)
20
// TODO: instant output (for some commands like `py ...`, `go ...`, qcalc)
20
21
21
// commands wishlist:
22
// commands wishlist:
43
44
44
    fn spawn(allocator: *std.mem.Allocator, argv: []const []const u8, max_output_bytes: usize) !ProcessWithOutput {
45
    fn spawn(allocator: *std.mem.Allocator, argv: []const []const u8, max_output_bytes: usize) !ProcessWithOutput {
45
        const child = try std.ChildProcess.init(argv, allocator);
46
        const child = try std.ChildProcess.init(argv, allocator);
47
        child.expand_arg0 = std.ChildProcess.Arg0Expand.expand;
46
        child.stdin_behavior = std.ChildProcess.StdIo.Ignore;
48
        child.stdin_behavior = std.ChildProcess.StdIo.Ignore;
47
        child.stdout_behavior = std.ChildProcess.StdIo.Pipe;
49
        child.stdout_behavior = std.ChildProcess.StdIo.Pipe;
48
        child.stderr_behavior = std.ChildProcess.StdIo.Pipe;
50
        child.stderr_behavior = std.ChildProcess.StdIo.Pipe;
59
        }
61
        }
60
    }
62
    }
61
63
62
    fn stdout(self: ProcessWithOutput) []u8 {
64
    fn stdout(self: *ProcessWithOutput) []u8 {
63
        return self.stdout_buf.items;
65
        return self.stdout_buf.items;
64
    }
66
    }
65
67
66
    fn stderr(self: ProcessWithOutput) []u8 {
68
    fn stderr(self: *ProcessWithOutput) []u8 {
67
        return self.stderr_buf.items;
69
        return self.stderr_buf.items;
68
    }
70
    }
69
71
151
153
152
const RegexRunner = struct {
154
const RegexRunner = struct {
153
    run_always: bool,
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
        if (!self.run_always and !is_confirmed) {
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
    const keyboardState = c.SDL_GetKeyboardState(null);
324
    const keyboardState = c.SDL_GetKeyboardState(null);
245
325
246
    c.SDL_StartTextInput();
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
    var quit = false;
333
    var quit = false;
251
    var skip: i32 = 0;
334
    var skip: i32 = 0;
252
    var num_lines: i32 = 0;
335
    var num_lines: i32 = 0;
253
    while (!quit) {
336
    while (!quit) {
254
        var event: c.SDL_Event = undefined;
337
        var event: c.SDL_Event = undefined;
338
        var changed = false;
339
        var confirmed = false;
255
        while (c.SDL_PollEvent(&event) != 0) {
340
        while (c.SDL_PollEvent(&event) != 0) {
256
            const ctrlPressed = (keyboardState[c.SDL_SCANCODE_LCTRL] != 0);
341
            const ctrlPressed = (keyboardState[c.SDL_SCANCODE_LCTRL] != 0);
257
            switch (event.@"type") {
342
            switch (event.@"type") {
302
                                    msg[max_chars] = 0;
387
                                    msg[max_chars] = 0;
303
                                }
388
                                }
304
                                c.SDL_free(clipboard_text);
389
                                c.SDL_free(clipboard_text);
390
391
                                changed = true;
305
                            },
392
                            },
306
                            else => {},
393
                            else => {},
307
                        }
394
                        }
313
                            c.SDLK_BACKSPACE => {
400
                            c.SDLK_BACKSPACE => {
314
                                pos = if (pos == 0) max_chars - 1 else (pos - 1) % (max_chars - 1);
401
                                pos = if (pos == 0) max_chars - 1 else (pos - 1) % (max_chars - 1);
315
                                msg[pos] = '_';
402
                                msg[pos] = '_';
403
                                changed = true;
316
                            },
404
                            },
317
                            c.SDLK_RETURN => {
405
                            c.SDLK_RETURN => {
318
                                skip = 0;
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
                            c.SDLK_UP => {
410
                            c.SDLK_UP => {
336
                                if (skip > 0) {
411
                                if (skip > 0) {
367
                        c.SDL_Log("input: '%s' at %d", event.text.text, pos);
442
                        c.SDL_Log("input: '%s' at %d", event.text.text, pos);
368
                        msg[pos] = event.text.text[0];
443
                        msg[pos] = event.text.text[0];
369
                        pos = (pos + 1) % (max_chars - 1);
444
                        pos = (pos + 1) % (max_chars - 1);
445
446
                        changed = true;
370
                    }
447
                    }
371
                },
448
                },
372
                else => {},
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
        _ = c.SDL_SetRenderDrawColor(renderer, 0, 0, 0, 100);
461
        _ = c.SDL_SetRenderDrawColor(renderer, 0, 0, 0, 100);
377
        //_ = c.SDL_SetRenderDrawBlendMode(renderer, c.SDL_BlendMode.SDL_BLENDMODE_BLEND);
462
        //_ = c.SDL_SetRenderDrawBlendMode(renderer, c.SDL_BlendMode.SDL_BLENDMODE_BLEND);
378
        _ = c.SDL_RenderClear(renderer);
463
        _ = c.SDL_RenderClear(renderer);
388
        _ = c.SDL_RenderCopy(renderer, texture, null, &c.SDL_Rect{ .x = 0, .y = 0, .w = @intCast(c_int, msg.len) * glyph_width, .h = glyph_height });
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
        var i: c_int = 1;
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
        _ = c.SDL_RenderPresent(renderer);
505
        _ = c.SDL_RenderPresent(renderer);
419
506
420
        c.SDL_Delay(16);
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
fn runCommand(raw_cmd: []const u8, allocator: *std.mem.Allocator) ![]const u8 {
516
fn runCommand(raw_cmd: []const u8, allocator: *std.mem.Allocator) ![]const u8 {