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