mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-08-25 16:41:44 +00:00
feat: expand tildes in config theme path to HOME (#13840)
When loading a theme from a path that includes a tilde: ``` theme="~/.cache/wal/colors-ghostty" ``` ghostty currently fails with the following error: ``` cannot include path separators unless it is an absolute path ``` This PR tries to expand the ~ of the path. If there is no ~ or expansion fails, it falls back to the unexpanded value.
This commit is contained in:
@@ -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, ""));
|
||||
|
||||
Reference in New Issue
Block a user