diff options
Diffstat (limited to 'src/ninep/ctl.zig')
| -rw-r--r-- | src/ninep/ctl.zig | 48 |
1 files changed, 44 insertions, 4 deletions
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index f8623903..1f0195d0 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -540,12 +540,47 @@ fn sizeOf(line: []const u8) ?[2]u16 { /// 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. +/// A builtin's doc comment, its first sentence, from the source itself: a +/// line of /commands says what each word does without a second copy of it. +fn summaryOf(word: []const u8, buf: []u8) []const u8 { + const source = @embedFile("../builtins.zig"); + var needle_buf: [64]u8 = undefined; + const needle = std.fmt.bufPrint(&needle_buf, "\npub const {s} = struct", .{word}) catch return ""; + const at = std.mem.indexOf(u8, source, needle) orelse return ""; + // Back over the `///` lines just above it. + var start = at + 1; + while (start > 0) { + const prev_end = start - 1; + const prev_start = if (std.mem.lastIndexOfScalar(u8, source[0..prev_end], '\n')) |nl| nl + 1 else 0; + if (!std.mem.startsWith(u8, std.mem.trimStart(u8, source[prev_start..prev_end], " "), "///")) break; + start = prev_start; + } + var w = std.Io.Writer.fixed(buf); + var lines = std.mem.splitScalar(u8, source[start .. at + 1], '\n'); + while (lines.next()) |line| { + const text = std.mem.trim(u8, std.mem.trimStart(u8, std.mem.trimStart(u8, line, " "), "/"), " "); + if (text.len == 0) continue; + if (w.buffered().len > 0) w.writeByte(' ') catch break; + w.writeAll(text) catch break; + } + const all = w.buffered(); + // The first sentence, whole: to a `.` that ends one, or the whole cut + // between words. + var i: usize = 0; + while (std.mem.indexOfScalarPos(u8, all, i, '.')) |dot| : (i = dot + 1) { + if (dot + 1 == all.len or all[dot + 1] == ' ') return all[0 .. dot + 1]; + } + return all; +} + fn writeCommands(w: *std.Io.Writer) !void { for (std.enums.values(Builtin)) |b| { + // Edit is a pane's word the root's ctl takes too, at the active pane. + const scope: []const u8 = if (b == .Edit) "both" else if (builtins.registry.scope(b) == .session) "root" else "pane"; try w.print("{s}{s} {s}", .{ @tagName(b), if (builtins.registry.takesArg(b)) " arg" else "", - if (builtins.registry.scope(b) == .session) "root" else "pane", + scope, }); // A setting that chooses among words: the words, comma-joined. Bare, // it steps to the next of them (a two-valued one flips). @@ -555,6 +590,10 @@ fn writeCommands(w: *std.Io.Writer) !void { var first = true; while (parts.next()) |v| : (first = false) try w.print("{s}{s}", .{ if (first) "" else ",", v }); }; + // Then ` -- ` and what it does, when its source says. + var summary_buf: [2048]u8 = undefined; + const summary = summaryOf(@tagName(b), &summary_buf); + if (summary.len > 0) try w.print(" -- {s}", .{@import("../Messages.zig").clip(summary, 160)}); try w.writeByte('\n'); } } @@ -786,12 +825,13 @@ test "commands lists every builtin with its argument and the ctl that takes it" 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.expectEqualStrings(if (b == .Edit) "both" else if (builtins.registry.scope(b) == .session) "root" else "pane", words.next().?); if (config.Runtime.find(@tagName(b))) |s| if (config.Runtime.choices(s.action) != null) try testing.expect(words.next() != null); - try testing.expect(words.next() == null); + // Then, where the source has one, ` -- ` and what it does. + if (words.next()) |dashes| try testing.expectEqualStrings("--", dashes); } 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 on,off\n", "\nPlacement arg root acme,pardes\n", "\nUndo pane\n", "\nRedo pane\n" }) |row| + for ([_][]const u8{ "\nDel arg pane -- ", "\nNewcol root -- ", "\nSave arg pane", "\nVerbose arg root on,off", "\nPlacement arg root acme,pardes", "\nUndo pane -- ", "\nEdit arg both -- acme's Edit" }) |row| try testing.expect(std.mem.indexOf(u8, listed.bytes, row) != null); } |
