Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(config): generate default template when config file is not found #3460

Merged
merged 1 commit into from
Dec 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 51 additions & 13 deletions src/config/Config.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2668,18 +2668,40 @@ pub fn loadFile(self: *Config, alloc: Allocator, path: []const u8) !void {
try self.expandPaths(std.fs.path.dirname(path).?);
}

pub const OptionalFileAction = enum { loaded, not_found, @"error" };

/// Load optional configuration file from `path`. All errors are ignored.
pub fn loadOptionalFile(self: *Config, alloc: Allocator, path: []const u8) void {
self.loadFile(alloc, path) catch |err| switch (err) {
error.FileNotFound => std.log.info(
"optional config file not found, not loading path={s}",
.{path},
),
else => std.log.warn(
"error reading optional config file, not loading err={} path={s}",
.{ err, path },
),
};
///
/// Returns the action that was taken.
pub fn loadOptionalFile(
self: *Config,
alloc: Allocator,
path: []const u8,
) OptionalFileAction {
if (self.loadFile(alloc, path)) {
return .loaded;
} else |err| switch (err) {
error.FileNotFound => return .not_found,
else => {
std.log.warn(
"error reading optional config file, not loading err={} path={s}",
.{ err, path },
);

return .@"error";
},
}
}

fn writeConfigTemplate(path: []const u8) !void {
log.info("creating template config file: path={s}", .{path});
const file = try std.fs.createFileAbsolute(path, .{});
defer file.close();
try std.fmt.format(
file.writer(),
@embedFile("./config-template"),
.{ .path = path },
);
}

/// Load configurations from the default configuration files. The default
Expand All @@ -2688,14 +2710,30 @@ pub fn loadOptionalFile(self: *Config, alloc: Allocator, path: []const u8) void
/// On macOS, `$HOME/Library/Application Support/$CFBundleIdentifier/config`
/// is also loaded.
pub fn loadDefaultFiles(self: *Config, alloc: Allocator) !void {
// Load XDG first
const xdg_path = try internal_os.xdg.config(alloc, .{ .subdir = "ghostty/config" });
defer alloc.free(xdg_path);
self.loadOptionalFile(alloc, xdg_path);
const xdg_action = self.loadOptionalFile(alloc, xdg_path);

// On macOS load the app support directory as well
if (comptime builtin.os.tag == .macos) {
const app_support_path = try internal_os.macos.appSupportDir(alloc, "config");
defer alloc.free(app_support_path);
self.loadOptionalFile(alloc, app_support_path);
const app_support_action = self.loadOptionalFile(alloc, app_support_path);

// If both files are not found, then we create a template file.
// For macOS, we only create the template file in the app support
if (app_support_action == .not_found and xdg_action == .not_found) {
writeConfigTemplate(app_support_path) catch |err| {
log.warn("error creating template config file err={}", .{err});
};
}
} else {
if (xdg_action == .not_found) {
writeConfigTemplate(xdg_path) catch |err| {
log.warn("error creating template config file err={}", .{err});
};
}
}
}

Expand Down
43 changes: 43 additions & 0 deletions src/config/config-template
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# This is the configuration file for Ghostty.
#
# This template file has been automatically created at the following
# path since Ghostty couldn't find any existing config files on your system:
#
# {[path]s}
#
# The template does not set any default options, since Ghostty ships
# with sensible defaults for all options. Users should only need to set
# options that they want to change from the default.
#
# Run `ghostty +show-config --default --docs` to view a list of
# all available config options and their default values.
#
# Additionally, each config option is also explained in detail
# on Ghostty's website, at https://ghostty.org/docs/config.

# Config syntax crash course
# ==========================
# # The config file consists of simple key-value pairs,
# # separated by equals signs.
# font-family = Iosevka
# window-padding-x = 2
#
# # Spacing around the equals sign does not matter.
# # All of these are identical:
# key=value
# key= value
# key =value
# key = value
#
# # Any line beginning with a # is a comment. It's not possible to put
# # a comment after a config option, since it would be interpreted as a
# # part of the value. For example, this will have a value of "#123abc":
# background = #123abc
#
# # Empty values are used to reset config keys to default.
# key =
#
# # Some config options have unique syntaxes for their value,
# # which is explained in the docs for that config option.
# # Just for example:
# resize-overlay-duration = 4s 200ms
8 changes: 1 addition & 7 deletions src/config/edit.zig
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,7 @@ pub fn open(alloc_gpa: Allocator) !void {
///
/// The allocator must be an arena allocator. No memory is freed by this
/// function and the resulting path is not all the memory that is allocated.
///
/// NOTE: WHY IS THIS INLINE? This is inline because when this is not
/// inline then Zig 0.13 crashes [most of the time] when trying to compile
/// this file. This is a workaround for that issue. This function is only
/// called from one place that is not performance critical so it is fine
/// to be inline.
inline fn configPath(alloc_arena: Allocator) ![]const u8 {
fn configPath(alloc_arena: Allocator) ![]const u8 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just an unrelated change I snuck in here because I noticed it.

const paths: []const []const u8 = try configPathCandidates(alloc_arena);
assert(paths.len > 0);

Expand Down