diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-29 11:46:38 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:16 -0300 |
| commit | cef529dc8cd080198f79d2b26bffbe9c7cf3e8bd (patch) | |
| tree | b6f98f447572d9e9c94c27981b737e897268d1a0 /src/colors.zig | |
| parent | 261eb7ac3f090c8ce47ad47c0f58f824a9b162a6 (diff) | |
| download | pardes-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/colors.zig')
| -rw-r--r-- | src/colors.zig | 52 |
1 files changed, 48 insertions, 4 deletions
diff --git a/src/colors.zig b/src/colors.zig index da074765..aa69fe6d 100644 --- a/src/colors.zig +++ b/src/colors.zig @@ -711,19 +711,63 @@ fn invalidateThemeDependentRasters(p: *Pardes) void { }; } -/// Themes whose names share `name`'s first letter, a line of them, for a -/// Theme given a name there is none of: all of them (Themes) are too many -/// for a message or a refused write. +/// Themes near `name`, a line of them, for a Theme given a name there is +/// none of (all of them, Themes, are too many for a refused write): every +/// one sharing its first letter when they fit in `buf`, else the 8 nearest. pub fn themesNear(buf: []u8, name: []const u8) []const u8 { var w = std.Io.Writer.fixed(buf); for (themes) |t| { if (name.len > 0 and std.ascii.toLower(t.name[0]) != std.ascii.toLower(name[0])) continue; - if (w.buffered().len + t.name.len + 1 > buf.len) break; w.print("{s}{s}", .{ if (w.buffered().len > 0) " " else "", t.name }) catch break; + } else if (w.buffered().len > 0) return w.buffered(); + // The nearest, most alike first: ponytail, a plain score (shared + // prefix, the name inside, its letters in order), no edit distance. + var best: [8]usize = undefined; + var scores: [8]usize = undefined; + var n: usize = 0; + for (themes, 0..) |t, i| { + const s = likeness(t.name, name); + var at = n; + while (at > 0 and scores[at - 1] < s) at -= 1; + if (at == best.len) continue; + const last = @min(n, best.len - 1); + var k = last; + while (k > at) : (k -= 1) { + best[k] = best[k - 1]; + scores[k] = scores[k - 1]; + } + best[at] = i; + scores[at] = s; + n = @min(n + 1, best.len); } + w = std.Io.Writer.fixed(buf); + for (best[0..n]) |i| w.print("{s}{s}", .{ if (w.buffered().len > 0) " " else "", themes[i].name }) catch break; return w.buffered(); } +fn likeness(theme_name: []const u8, name: []const u8) usize { + var prefix: usize = 0; + while (prefix < @min(theme_name.len, name.len) and std.ascii.toLower(theme_name[prefix]) == std.ascii.toLower(name[prefix])) prefix += 1; + // Its letters found in order, one not there passed over. + var in_order: usize = 0; + var pos: usize = 0; + for (name) |c| { + const at = for (theme_name[pos..], pos..) |t, i| { + if (std.ascii.toLower(t) == std.ascii.toLower(c)) break i; + } else continue; + in_order += 1; + pos = at + 1; + } + const inside: usize = if (name.len > 0 and std.ascii.indexOfIgnoreCase(theme_name, name) != null) name.len * 4 else 0; + return prefix * 3 + in_order + inside; +} + +/// The theme named `name`, whatever its case. +pub fn themeIndex(name: []const u8) ?usize { + for (themes, 0..) |t, i| if (std.ascii.eqlIgnoreCase(t.name, name)) return i; + return null; +} + pub fn setThemeIndex(p: *Pardes, index: usize) void { if (index >= themes.len or (p.custom_theme == null and p.theme_file_path.get().len == 0 and |
