summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/colors.zig21
-rw-r--r--src/ninep/ctl.zig26
-rw-r--r--src/pardes.zig3
3 files changed, 49 insertions, 1 deletions
diff --git a/src/colors.zig b/src/colors.zig
index b75cdc3d..d04c009e 100644
--- a/src/colors.zig
+++ b/src/colors.zig
@@ -9,6 +9,7 @@ const animation = @import("animation.zig");
const Color = @import("surface.zig").Color;
const FontRole = @import("surface.zig").FontRole;
const config = @import("config.zig");
+const limits = @import("memory.zig").limits;
const pdf_enabled = panes.Pdf.enabled;
const platform = pardes.platform;
const theme_animation = @import("pardes_config").theme_animation;
@@ -739,7 +740,7 @@ pub fn requestThemeFile(p: *Pardes, id: usize, argument: []const u8) void {
/// /ctl and DumpConfig say in place of `Theme <name>`, since the name
/// inside the file is no theme `Theme` knows.
pub fn loadedThemeFile(p: *const Pardes) ?[]const u8 {
- const path = p.theme_file_path.get();
+ const path = p.theme_file_loaded.get();
return if (p.custom_theme != null and path.len > 0) path else null;
}
@@ -769,6 +770,20 @@ fn themeFileError(p: *Pardes, request: ThemeFileRequest, err: anyerror) void {
var cut: [200]u8 = undefined;
const path = @import("ninep/tree.zig").fitErr(request.path, &cut);
p.reportError(request.pane, std.fmt.bufPrint(&said, "theme file {s}", .{path}) catch "theme file", err);
+ revertThemeFile(p);
+}
+
+/// A ThemeFile that did not load changes nothing: the request goes back to
+/// the file last loaded (watched again), or to none, so /ctl and DumpConfig
+/// still read what replays. A bad save of the loaded file itself keeps it.
+fn revertThemeFile(p: *Pardes) void {
+ const loaded = p.theme_file_loaded.get();
+ if (std.mem.eql(u8, loaded, p.theme_file_path.get())) return;
+ var keep: [limits.host_path_cap]u8 = undefined;
+ @memcpy(keep[0..loaded.len], loaded);
+ if (loaded.len == 0) p.theme_file_path.clear() else _ = p.theme_file_path.set(keep[0..loaded.len]);
+ const generation = nextThemeFileGeneration(p);
+ p.emit(.{ .theme_file = .{ .generation = generation, .on = loaded.len > 0 } });
}
pub fn loadThemeFile(p: *Pardes, generation: u32, bytes: []const u8, animate: bool) bool {
@@ -804,6 +819,9 @@ pub fn loadThemeFile(p: *Pardes, generation: u32, bytes: []const u8, animate: bo
const old = p.custom_theme;
p.custom_theme = parsed;
if (old) |theme_value| std.zon.parse.free(p.gpa, theme_value);
+ var settled: [limits.host_path_cap]u8 = undefined;
+ @memcpy(settled[0..request.path.len], request.path);
+ _ = p.theme_file_loaded.set(settled[0..request.path.len]);
invalidateThemeDependentRasters(p);
_ = p.scratch.reset(.retain_capacity);
p.sync();
@@ -912,6 +930,7 @@ pub fn setThemeIndex(p: *Pardes, index: usize) void {
p.custom_theme = null;
std.zon.parse.free(p.gpa, theme_value);
}
+ p.theme_file_loaded.clear();
if (p.theme_file_path.get().len > 0) {
p.theme_file_path.clear();
const generation = nextThemeFileGeneration(p);
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig
index a4979f7d..e963da3f 100644
--- a/src/ninep/ctl.zig
+++ b/src/ninep/ctl.zig
@@ -2034,6 +2034,32 @@ test "DumpConfig's whole report writes back to the root ctl, line by line, with
}
}
+test "a ThemeFile that fails to load changes nothing: the root ctl still names the one loaded, or none" {
+ if (comptime !pardes.hosted) return;
+ const gpa = testing.allocator;
+ const p = try withFile(gpa, "x\n");
+ defer p.deinit();
+ const root_ctl = @intFromEnum(tree.TopFile.ctl);
+ // None loaded: a failed one leaves Theme as it was.
+ _ = wr(p, root_ctl, "ThemeFile /tmp/pardes-no-such-theme.zon\n");
+ pardes.colors.failThemeFile(p, p.theme_file_generation, error.FileNotFound);
+ try testing.expect(std.mem.indexOf(u8, rd(p, root_ctl, 0, 1 << 16).bytes, "ThemeFile") == null);
+ try testing.expectEqualStrings("", p.theme_file_path.get());
+ // One loaded, then another that fails: still the first, on every read.
+ const good = "/tmp/pardes-good-theme.zon";
+ _ = wr(p, root_ctl, "ThemeFile " ++ good ++ "\n");
+ var custom = pardes.themes[0];
+ custom.name = "good";
+ var encoded: std.Io.Writer.Allocating = .init(gpa);
+ defer encoded.deinit();
+ try std.zon.stringify.serialize(custom, .{}, &encoded.writer);
+ try testing.expect(pardes.colors.loadThemeFile(p, p.theme_file_generation, encoded.written(), false));
+ _ = wr(p, root_ctl, "ThemeFile /tmp/pardes-bad-theme.zon\n");
+ try testing.expect(!pardes.colors.loadThemeFile(p, p.theme_file_generation, ".{ .name = ", false));
+ try testing.expect(std.mem.indexOf(u8, rd(p, root_ctl, 0, 1 << 16).bytes, "ThemeFile " ++ good ++ "\n") != null);
+ try testing.expectEqualStrings(good, p.theme_file_path.get());
+}
+
test "after a ThemeFile the root ctl and DumpConfig say ThemeFile and its path, which writes back" {
if (comptime !pardes.hosted) return;
const gpa = testing.allocator;
diff --git a/src/pardes.zig b/src/pardes.zig
index 5d0ab7e8..945fcafa 100644
--- a/src/pardes.zig
+++ b/src/pardes.zig
@@ -4351,6 +4351,9 @@ pub const Pardes = struct {
/// bodyChrome's: the theme it was worked out for, and it.
body_chrome: ?struct { theme: Theme, chrome: ChromeTheme } = null,
theme_file_path: config.Runtime.Text(limits.host_path_cap) = .{},
+ /// The ThemeFile last loaded whole: what /ctl and DumpConfig name, and
+ /// what a failed ThemeFile goes back to (colors.revertThemeFile).
+ theme_file_loaded: config.Runtime.Text(limits.host_path_cap) = .{},
theme_file_generation: u32 = 0,
theme_file_pane: u8 = 0,
chrome_animation: colors.ChromeAnimation = colors.ChromeAnimation.init(colors.initial_chrome),