summaryrefslogtreecommitdiff
path: root/src/colors.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/colors.zig')
-rw-r--r--src/colors.zig52
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