summaryrefslogtreecommitdiff
path: root/src/ninep
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-29 11:46:38 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:16 -0300
commitcef529dc8cd080198f79d2b26bffbe9c7cf3e8bd (patch)
treeb6f98f447572d9e9c94c27981b737e897268d1a0 /src/ninep
parent261eb7ac3f090c8ce47ad47c0f58f824a9b162a6 (diff)
downloadpardes-cef529dc8cd080198f79d2b26bffbe9c7cf3e8bd.tar.gz
pardes-cef529dc8cd080198f79d2b26bffbe9c7cf3e8bd.zip
Theme takes a name in any case, and a miss names its nearest themes
Names were case-sensitive, and a miss listed only the first letter's themes that fitted 40 bytes, 4 of the many d ones. It now lists every theme sharing the first letter when they fit the 128 bytes an Rerror carries, else the nearest by a plain likeness score (shared prefix, the name inside, its letters in order). Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/ninep')
-rw-r--r--src/ninep/ctl.zig40
1 files changed, 32 insertions, 8 deletions
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig
index ea0a0bc6..68b62186 100644
--- a/src/ninep/ctl.zig
+++ b/src/ninep/ctl.zig
@@ -408,13 +408,15 @@ fn checkBuiltin(p: *Pardes, req: Req, line: []const u8, scope: builtins.Scope) ?
if (arg.len == 0 and builtins.registry.requiresArg(b)) return refuse(p, req, "wrong #args in control message", line);
const setting = config.Runtime.find(word) orelse return null;
const takes = switch (setting.action) {
- .theme => for (pardes.themes) |t| {
- if (std.mem.eql(u8, t.name, arg)) break true;
- } else {
- // `refuse` quotes the line after it, in what is left of 128.
- var near: [40]u8 = undefined;
- var why: [104]u8 = undefined;
- return refuse(p, req, std.fmt.bufPrint(&why, "bad value in control message; like it: {s}; Themes lists all", .{pardes.colors.themesNear(&near, arg)}) catch "bad value in control message", line);
+ .theme => if (pardes.colors.themeIndex(arg) != null) true else {
+ // `refuse` quotes the line after it, all in the 128 bytes an
+ // Rerror carries: the names get what is left.
+ const head = "bad value in control message; like it: ";
+ const tail = "; Themes lists all";
+ var near: [128]u8 = undefined;
+ const room = @import("cloud9").fs.errmax -| (head.len + tail.len + line.len + 3);
+ var why: [256]u8 = undefined;
+ return refuse(p, req, std.fmt.bufPrint(&why, head ++ "{s}" ++ tail, .{pardes.colors.themesNear(near[0..@min(room, near.len)], arg)}) catch "bad value in control message", line);
},
.font => config.Runtime.FontSpec.parse(arg) != null,
else => probe: {
@@ -1614,12 +1616,34 @@ test "Joincol with no column to the right and Theme with no such theme say so" {
const themed = wr(p, root_ctl, "Theme drak\n");
try testing.expectEqual(E.INVAL, themed.errno());
try testing.expectStringStartsWith(themed.reply.ename, "bad value in control message; like it: ");
- try testing.expect(std.mem.indexOf(u8, themed.reply.ename, " dark") != null or std.mem.indexOf(u8, themed.reply.ename, ": dark") != null);
+ try testing.expect(std.mem.indexOf(u8, themed.reply.ename, ": dr") != null);
try testing.expect(std.mem.endsWith(u8, themed.reply.ename, "\"Theme drak\""));
// A click on the word says it on the message row.
_ = wr(p, Node.of(serialOf(p), .exec), "Theme drak\n");
const pane = p.panes[p.active].?;
try testing.expect(std.mem.indexOf(u8, pane.msg[0..pane.msg_len], "no theme \"drak\"") != null);
+ // Within the 128 bytes an Rerror carries, the quoted line kept.
+ try testing.expect(themed.reply.ename.len <= 128);
+ // A name in any case is the theme.
+ try testing.expectEqual(Status.ok, wr(p, root_ctl, "Theme DRACULA\n").reply.status);
+ try testing.expectEqualStrings("dracula", pardes.themes[p.settings.theme].name);
+ // A misspelling far from any first letter's list still finds its like.
+ var near: [170]u8 = undefined;
+ try testing.expect(std.mem.indexOf(u8, pardes.colors.themesNear(&near, "9dracula"), "dracula") != null);
+ // Every theme sharing the first letter, when they fit.
+ var tested = false;
+ for ("abcdefghijklmnopqrstuvwxyz") |letter| {
+ var count: usize = 0;
+ var len: usize = 0;
+ for (pardes.themes) |t| if (t.name[0] == letter) {
+ count += 1;
+ len += t.name.len + 1;
+ };
+ if (count < 2 or len > near.len) continue;
+ try testing.expectEqual(count, std.mem.count(u8, pardes.colors.themesNear(&near, &.{letter}), " ") + 1);
+ tested = true;
+ }
+ try testing.expect(tested);
}
test "a look after Newcol fills the empty column the keyboard is on" {