diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-28 09:50:21 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:14 -0300 |
| commit | 12d387e4a03204a2b6d0c51633e228063e883fad (patch) | |
| tree | 9d1af954cacbbc90f60bd85499088865c786f993 /src/ninep/ctl.zig | |
| parent | f5eaddb2cf28a9b72d5dd5b3b10502a316d4de85 (diff) | |
| download | pardes-12d387e4a03204a2b6d0c51633e228063e883fad.tar.gz pardes-12d387e4a03204a2b6d0c51633e228063e883fad.zip | |
A root commands file lists every builtin, its argument and which ctl takes it
A script could learn the builtin words only from source or the Help
window. /commands is generated from the registry: one line per builtin,
Word or Word arg, then root or pane for the ctl that takes it, so it is
always this build's own list.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/ninep/ctl.zig')
| -rw-r--r-- | src/ninep/ctl.zig | 50 |
1 files changed, 50 insertions, 0 deletions
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index b43d7b20..14f1ae4f 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -308,6 +308,35 @@ pub fn writeRoot(p: *Pardes, req: Req) Reply { return .{ .tag = req.tag, .written = @intCast(req.data.len) }; } +// ---- /commands ---- + +/// Every builtin the registry holds, one a line: its word, `arg` when it +/// takes one, and which ctl takes it, `root` or `pane` -- the registry +/// itself, so a new builtin is listed the moment it exists. +fn writeCommands(w: *std.Io.Writer) !void { + for (std.enums.values(Builtin)) |b| { + try w.print("{s}{s} {s}\n", .{ + @tagName(b), + if (builtins.registry.takesArg(b)) " arg" else "", + if (builtins.registry.scope(b) == .session) "root" else "pane", + }); + } +} + +pub fn commandsLen() u64 { + var buf: [256]u8 = undefined; + var counting: std.Io.Writer.Discarding = .init(&buf); + writeCommands(&counting.writer) catch return 0; + return counting.fullCount(); +} + +pub fn readCommands(p: *Pardes, req: Req) Reply { + var text: std.Io.Writer.Allocating = .init(p.scratch.allocator()); + writeCommands(&text.writer) catch return Reply.fail(req.tag, E.NOMEM); + p.fs.stage(p.gpa).appendSlice(p.gpa, text.written()) catch return Reply.fail(req.tag, E.NOMEM); + return tree.stagedReply(p, req); +} + // ---- /focus ---- /// The pane with the keyboard, which a write of a serial moves, the way @@ -452,6 +481,27 @@ test "pane ctl read is index's five fields plus width in cells, font, tab width try testing.expectEqualStrings("'it''s'", w.buffered()); } +test "commands lists every builtin with its argument and the ctl that takes it" { + const p = try withFile(testing.allocator, "x\n"); + defer p.deinit(); + const node = @intFromEnum(tree.TopFile.commands); + const listed = rd(p, node, 0, 1 << 16); + try testing.expectEqual(@as(u64, listed.bytes.len), call(p, .{ .tag = 1, .op = .getattr, .node = node }).reply.attr.size); + try testing.expectEqual(E.PERM, wr(p, node, "Del\n").errno()); + var lines: usize = 0; + var it = std.mem.tokenizeScalar(u8, listed.bytes, '\n'); + while (it.next()) |line| : (lines += 1) { + var words = std.mem.tokenizeScalar(u8, line, ' '); + const b = std.meta.stringToEnum(Builtin, words.next().?).?; + if (builtins.registry.takesArg(b)) try testing.expectEqualStrings("arg", words.next().?); + try testing.expectEqualStrings(if (builtins.registry.scope(b) == .session) "root" else "pane", words.next().?); + try testing.expect(words.next() == null); + } + try testing.expectEqual(std.enums.values(Builtin).len, lines); + for ([_][]const u8{ "\nDel arg pane\n", "\nNewcol root\n", "\nSave arg pane\n", "\nVerbose arg root\n" }) |row| + try testing.expect(std.mem.indexOf(u8, listed.bytes, row) != null); +} + test "focus reads the pane with the keyboard, and a serial written to it moves the keyboard there" { const p = try withFile(testing.allocator, "x\n"); defer p.deinit(); |
