From 4dfbb79f8cafade6b8f386ff49016f16f970ba74 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 22 Nov 2023 21:34:26 -0800 Subject: [PATCH] config: load themes --- src/config/Config.zig | 79 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/src/config/Config.zig b/src/config/Config.zig index 0a3236174..e8ce156b7 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -7,6 +7,7 @@ const builtin = @import("builtin"); const assert = std.debug.assert; const Allocator = std.mem.Allocator; const ArenaAllocator = std.heap.ArenaAllocator; +const global_state = &@import("../main.zig").state; const fontpkg = @import("../font/main.zig"); const inputpkg = @import("../input.zig"); const terminal = @import("../terminal/main.zig"); @@ -1326,7 +1327,85 @@ fn expandPaths(self: *Config, base: []const u8) !void { } } +fn loadTheme(self: *Config, theme: []const u8) !void { + const alloc = self._arena.?.allocator(); + const resources_dir = global_state.resources_dir orelse { + try self._errors.add(alloc, .{ + .message = "no resources directory found, themes will not work", + }); + return; + }; + + const path = try std.fs.path.join(alloc, &.{ + resources_dir, + "themes", + theme, + }); + + const cwd = std.fs.cwd(); + var file = cwd.openFile(path, .{}) catch |err| { + switch (err) { + error.FileNotFound => try self._errors.add(alloc, .{ + .message = try std.fmt.allocPrintZ( + alloc, + "theme \"{s}\" not found, path={s}", + .{ theme, path }, + ), + }), + + else => try self._errors.add(alloc, .{ + .message = try std.fmt.allocPrintZ( + alloc, + "failed to load theme \"{s}\": {}", + .{ theme, err }, + ), + }), + } + return; + }; + defer file.close(); + + // From this point onwards, we load the theme and do a bit of a dance + // to achive two separate goals: + // + // (1) We want the theme to be loaded and our existing config to + // override the theme. So we need to load the theme and apply + // our config on top of it. + // + // (2) We want to free existing memory that we aren't using anymore + // as a result of reloading the configuration. + // + // Point 2 is strictly a result of aur approach to point 1. + + // Keep track of our input length prior ot loading the theme + // so that we can replay the previous config to override values. + const input_len = self._inputs.items.len; + + // Load into a new configuration so that we can free the existing memory. + const alloc_gpa = self._arena.?.child_allocator; + var new_config = try default(alloc_gpa); + errdefer new_config.deinit(); + + // Load our theme + var buf_reader = std.io.bufferedReader(file.reader()); + var iter = cli.args.lineIterator(buf_reader.reader()); + try new_config.loadIter(alloc_gpa, &iter); + + // Replay our previous inputs so that we can override values + // from the theme. + var slice_it = cli.args.sliceIterator(self._inputs.items[0..input_len]); + try new_config.loadIter(alloc_gpa, &slice_it); + + // Success, swap our new config in and free the old. + self.deinit(); + self.* = new_config; +} + pub fn finalize(self: *Config) !void { + // We always load the theme first because it may set other fields + // in our config. + if (self.theme) |theme| try self.loadTheme(theme); + // If we have a font-family set and don't set the others, default // the others to the font family. This way, if someone does // --font-family=foo, then we try to get the stylized versions of