diff --git a/src/config/Config.zig b/src/config/Config.zig index 993a4f6e1..5b2466d01 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -4517,6 +4517,29 @@ fn expandPaths(self: *Config, base: []const u8) !void { } } +/// Expand tilde paths to absolute paths to the user's home directory. +/// If expansion fails, an error is logged and the original path is returned. +fn expandHome(path: []const u8, buf: []u8) []const u8 { + if (!std.mem.startsWith(u8, path, "~/")) + return path; + + var environ_map = global.environMap() catch |err| { + log.warn("failed to get environment map for path \"{s}\": {}", .{ path, err }); + return path; + }; + defer environ_map.deinit(); + + return internal_os.expandHome( + global.io(), + &environ_map, + path, + buf, + ) catch |err| { + log.warn("failed to expand home directory in path \"{s}\": {}", .{ path, err }); + return path; + }; +} + fn loadTheme(self: *Config, theme: Theme) !void { // Load the correct theme depending on the conditional state. // Dark/light themes were programmed prior to conditional configuration @@ -4616,14 +4639,17 @@ fn loadTheme(self: *Config, theme: Theme) !void { /// Call this once after you are done setting configuration. This /// is idempotent but will waste memory if called multiple times. pub fn finalize(self: *Config) !void { + const alloc = self._arena.?.allocator(); + // We always load the theme first because it may set other fields // in our config. - if (self.theme) |theme| { + if (self.theme) |*theme| { + try theme.finalize(alloc); const different = !std.mem.eql(u8, theme.light, theme.dark); // Warning: loadTheme will deinit our existing config and replace // it so all memory from self prior to this point will be freed. - try self.loadTheme(theme); + try self.loadTheme(theme.*); // If we have different light vs dark mode themes, disable // window-theme = auto since that breaks it. @@ -4637,8 +4663,6 @@ pub fn finalize(self: *Config) !void { } } - const alloc = self._arena.?.allocator(); - // Used for a variety of defaults. See the function docs as well the // specific variable use sites for more details. const probable_cli = probableCliEnvironment(); @@ -5443,20 +5467,8 @@ pub const WorkingDirectory = union(enum) { else => return, }; - if (!std.mem.startsWith(u8, path, "~/")) return; - var buf: [std.fs.max_path_bytes]u8 = undefined; - const expanded = expanded: { - var environ_map = global.environMap() catch |err| break :expanded err; - defer environ_map.deinit(); - break :expanded internal_os.expandHome(global.io(), &environ_map, path, &buf); - } catch |err| { - log.warn( - "error expanding home directory for working-directory path={s}: {}", - .{ path, err }, - ); - return; - }; + const expanded = expandHome(path, &buf); if (std.mem.eql(u8, expanded, path)) return; self.* = .{ .path = try alloc.dupe(u8, expanded) }; @@ -9979,6 +9991,19 @@ pub const Theme = struct { }; } + /// Expand tilde paths in light/dark theme values. + pub fn finalize(self: *Theme, alloc: Allocator) Allocator.Error!void { + var buf: [std.fs.max_path_bytes]u8 = undefined; + + const light = expandHome(self.light, &buf); + if (!std.mem.eql(u8, light, self.light)) + self.light = try alloc.dupeZ(u8, light); + + const dark = expandHome(self.dark, &buf); + if (!std.mem.eql(u8, dark, self.dark)) + self.dark = try alloc.dupeZ(u8, dark); + } + /// Deep copy of the struct. Required by Config. pub fn clone(self: *const Theme, alloc: Allocator) Allocator.Error!Theme { return .{ @@ -10035,6 +10060,34 @@ pub const Theme = struct { try testing.expectEqualStrings("bar", v.dark); } + // Expand tilde to home + { + var environ_map = try testing.environ.createMap(alloc); + defer environ_map.deinit(); + + var home_buf: [std.fs.max_path_bytes]u8 = undefined; + const home = try internal_os.expandHome( + testing.io, + &environ_map, + "~/", + &home_buf, + ); + + var v: Theme = undefined; + try v.parseCLI(alloc, "light:~/foo, dark:~/bar"); + try v.finalize(alloc); + + var expected_buf: [std.fs.max_path_bytes]u8 = undefined; + try testing.expectEqualStrings( + try std.fmt.bufPrint(&expected_buf, "{s}foo", .{home}), + v.light, + ); + try testing.expectEqualStrings( + try std.fmt.bufPrint(&expected_buf, "{s}bar", .{home}), + v.dark, + ); + } + var v: Theme = undefined; try testing.expectError(error.ValueRequired, v.parseCLI(alloc, null)); try testing.expectError(error.ValueRequired, v.parseCLI(alloc, ""));