Add `.ghostty` extension to `config` (#8885)
Resolves #8689 For various reason, ghostty wants to have a unique file extension for the config files. The name was settled on `config.ghostty`. This will help with tooling. See #8438 (original discussion) for more details. This PR introduces the preferred default of `.ghostty` while still supporting the previous `config` file. If both files exist, a warning log is sent. The docs / website will need to be updated to reflect this change. > [!NOTE] > Only tested on macOS 26.0. --------- Co-authored-by: Mitchell Hashimoto <m@mitchellh.com>pull/9170/head
parent
37b3c27020
commit
cbeb6890c9
|
|
@ -14,7 +14,7 @@ struct SettingsView: View {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
Text("Coming Soon. 🚧").font(.title)
|
Text("Coming Soon. 🚧").font(.title)
|
||||||
Text("You can't configure settings in the GUI yet. To modify settings, " +
|
Text("You can't configure settings in the GUI yet. To modify settings, " +
|
||||||
"edit the file at $HOME/.config/ghostty/config and restart Ghostty.")
|
"edit the file at $HOME/.config/ghostty/config.ghostty and restart Ghostty.")
|
||||||
.multilineTextAlignment(.leading)
|
.multilineTextAlignment(.leading)
|
||||||
.lineLimit(nil)
|
.lineLimit(nil)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -225,7 +225,7 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyResources {
|
||||||
// 'ghostty.sublime-syntax' file from zig-out to the '~.config/bat/syntaxes'
|
// 'ghostty.sublime-syntax' file from zig-out to the '~.config/bat/syntaxes'
|
||||||
// directory. The syntax then needs to be mapped to the correct language in
|
// directory. The syntax then needs to be mapped to the correct language in
|
||||||
// the config file within the '~.config/bat' directory
|
// the config file within the '~.config/bat' directory
|
||||||
// (ex: --map-syntax "/Users/user/.config/ghostty/config:Ghostty Config").
|
// (ex: --map-syntax "/Users/user/.config/ghostty/config.ghostty:Ghostty Config").
|
||||||
{
|
{
|
||||||
const run = b.addRunArtifact(build_data_exe);
|
const run = b.addRunArtifact(build_data_exe);
|
||||||
run.addArg("+sublime");
|
run.addArg("+sublime");
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
# FILES
|
# FILES
|
||||||
|
|
||||||
_\$XDG_CONFIG_HOME/ghostty/config_
|
_\$XDG_CONFIG_HOME/ghostty/config.ghostty_
|
||||||
|
|
||||||
: Location of the default configuration file.
|
: Location of the default configuration file.
|
||||||
|
|
||||||
_\$HOME/Library/Application Support/com.mitchellh.ghostty/config_
|
_\$HOME/Library/Application Support/com.mitchellh.ghostty/config.ghostty_
|
||||||
|
|
||||||
: **On macOS**, location of the default configuration file. This location takes
|
: **On macOS**, location of the default configuration file. This location takes
|
||||||
precedence over the XDG environment locations.
|
precedence over the XDG environment locations.
|
||||||
|
|
||||||
_\$LOCALAPPDATA/ghostty/config_
|
_\$LOCALAPPDATA/ghostty/config.ghostty_
|
||||||
|
|
||||||
: **On Windows**, if _\$XDG_CONFIG_HOME_ is not set, _\$LOCALAPPDATA_ will be searched
|
: **On Windows**, if _\$XDG_CONFIG_HOME_ is not set, _\$LOCALAPPDATA_ will be searched
|
||||||
for configuration files.
|
for configuration files.
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
# FILES
|
# FILES
|
||||||
|
|
||||||
_\$XDG_CONFIG_HOME/ghostty/config_
|
_\$XDG_CONFIG_HOME/ghostty/config.ghostty_
|
||||||
|
|
||||||
: Location of the default configuration file.
|
: Location of the default configuration file.
|
||||||
|
|
||||||
_\$HOME/Library/Application Support/com.mitchellh.ghostty/config_
|
_\$HOME/Library/Application Support/com.mitchellh.ghostty/config.ghostty_
|
||||||
|
|
||||||
: **On macOS**, location of the default configuration file. This location takes
|
: **On macOS**, location of the default configuration file. This location takes
|
||||||
precedence over the XDG environment locations.
|
precedence over the XDG environment locations.
|
||||||
|
|
||||||
_\$LOCALAPPDATA/ghostty/config_
|
_\$LOCALAPPDATA/ghostty/config.ghostty_
|
||||||
|
|
||||||
: **On Windows**, if _\$XDG_CONFIG_HOME_ is not set, _\$LOCALAPPDATA_ will be searched
|
: **On Windows**, if _\$XDG_CONFIG_HOME_ is not set, _\$LOCALAPPDATA_ will be searched
|
||||||
for configuration files.
|
for configuration files.
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,11 @@
|
||||||
|
|
||||||
To configure Ghostty, you must use a configuration file. GUI-based configuration
|
To configure Ghostty, you must use a configuration file. GUI-based configuration
|
||||||
is on the roadmap but not yet supported. The configuration file must be placed
|
is on the roadmap but not yet supported. The configuration file must be placed
|
||||||
at `$XDG_CONFIG_HOME/ghostty/config`, which defaults to `~/.config/ghostty/config`
|
at `$XDG_CONFIG_HOME/ghostty/config.ghostty`, which defaults to `~/.config/ghostty/config.ghostty`
|
||||||
if the [XDG environment is not set](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html).
|
if the [XDG environment is not set](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html).
|
||||||
|
|
||||||
**If you are using macOS, the configuration file can also be placed at
|
**If you are using macOS, the configuration file can also be placed at
|
||||||
`$HOME/Library/Application Support/com.mitchellh.ghostty/config`.** This is the
|
`$HOME/Library/Application Support/com.mitchellh.ghostty/config.ghostty`.** This is the
|
||||||
default configuration location for macOS. It will be searched before any of the
|
default configuration location for macOS. It will be searched before any of the
|
||||||
XDG environment locations listed above.
|
XDG environment locations listed above.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,9 +30,9 @@ pub const Options = struct {
|
||||||
/// this yet.
|
/// this yet.
|
||||||
///
|
///
|
||||||
/// The filepath opened is the default user-specific configuration
|
/// The filepath opened is the default user-specific configuration
|
||||||
/// file, which is typically located at `$XDG_CONFIG_HOME/ghostty/config`.
|
/// file, which is typically located at `$XDG_CONFIG_HOME/ghostty/config.ghostty`.
|
||||||
/// On macOS, this may also be located at
|
/// On macOS, this may also be located at
|
||||||
/// `~/Library/Application Support/com.mitchellh.ghostty/config`.
|
/// `~/Library/Application Support/com.mitchellh.ghostty/config.ghostty`.
|
||||||
/// On macOS, whichever path exists and is non-empty will be prioritized,
|
/// On macOS, whichever path exists and is non-empty will be prioritized,
|
||||||
/// prioritizing the Application Support directory if neither are
|
/// prioritizing the Application Support directory if neither are
|
||||||
/// non-empty.
|
/// non-empty.
|
||||||
|
|
@ -73,7 +73,7 @@ fn runInner(alloc: Allocator, stderr: *std.Io.Writer) !u8 {
|
||||||
defer config.deinit();
|
defer config.deinit();
|
||||||
|
|
||||||
// Find the preferred path.
|
// Find the preferred path.
|
||||||
const path = try Config.preferredDefaultFilePath(alloc);
|
const path = try configpkg.preferredDefaultFilePath(alloc);
|
||||||
defer alloc.free(path);
|
defer alloc.free(path);
|
||||||
|
|
||||||
// We don't currently support Windows because we use the exec syscall.
|
// We don't currently support Windows because we use the exec syscall.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
|
|
||||||
|
const file_load = @import("config/file_load.zig");
|
||||||
const formatter = @import("config/formatter.zig");
|
const formatter = @import("config/formatter.zig");
|
||||||
pub const Config = @import("config/Config.zig");
|
pub const Config = @import("config/Config.zig");
|
||||||
pub const conditional = @import("config/conditional.zig");
|
pub const conditional = @import("config/conditional.zig");
|
||||||
|
|
@ -12,6 +13,7 @@ pub const ConditionalState = conditional.State;
|
||||||
pub const FileFormatter = formatter.FileFormatter;
|
pub const FileFormatter = formatter.FileFormatter;
|
||||||
pub const entryFormatter = formatter.entryFormatter;
|
pub const entryFormatter = formatter.entryFormatter;
|
||||||
pub const formatEntry = formatter.formatEntry;
|
pub const formatEntry = formatter.formatEntry;
|
||||||
|
pub const preferredDefaultFilePath = file_load.preferredDefaultFilePath;
|
||||||
|
|
||||||
// Field types
|
// Field types
|
||||||
pub const BoldColor = Config.BoldColor;
|
pub const BoldColor = Config.BoldColor;
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ const cli = @import("../cli.zig");
|
||||||
|
|
||||||
const conditional = @import("conditional.zig");
|
const conditional = @import("conditional.zig");
|
||||||
const Conditional = conditional.Conditional;
|
const Conditional = conditional.Conditional;
|
||||||
|
const file_load = @import("file_load.zig");
|
||||||
const formatterpkg = @import("formatter.zig");
|
const formatterpkg = @import("formatter.zig");
|
||||||
const themepkg = @import("theme.zig");
|
const themepkg = @import("theme.zig");
|
||||||
const url = @import("url.zig");
|
const url = @import("url.zig");
|
||||||
|
|
@ -2071,7 +2072,7 @@ keybind: Keybinds = .{},
|
||||||
|
|
||||||
/// When this is true, the default configuration file paths will be loaded.
|
/// When this is true, the default configuration file paths will be loaded.
|
||||||
/// The default configuration file paths are currently only the XDG
|
/// The default configuration file paths are currently only the XDG
|
||||||
/// config path ($XDG_CONFIG_HOME/ghostty/config).
|
/// config path ($XDG_CONFIG_HOME/ghostty/config.ghostty).
|
||||||
///
|
///
|
||||||
/// If this is false, the default configuration paths will not be loaded.
|
/// If this is false, the default configuration paths will not be loaded.
|
||||||
/// This is targeted directly at using Ghostty from the CLI in a way
|
/// This is targeted directly at using Ghostty from the CLI in a way
|
||||||
|
|
@ -3397,7 +3398,7 @@ pub fn loadIter(
|
||||||
/// `path` must be resolved and absolute.
|
/// `path` must be resolved and absolute.
|
||||||
pub fn loadFile(self: *Config, alloc: Allocator, path: []const u8) !void {
|
pub fn loadFile(self: *Config, alloc: Allocator, path: []const u8) !void {
|
||||||
assert(std.fs.path.isAbsolute(path));
|
assert(std.fs.path.isAbsolute(path));
|
||||||
var file = openFile(path) catch |err| switch (err) {
|
var file = file_load.open(path) catch |err| switch (err) {
|
||||||
error.NotAFile => {
|
error.NotAFile => {
|
||||||
log.warn(
|
log.warn(
|
||||||
"config-file {s}: not reading because it is not a file",
|
"config-file {s}: not reading because it is not a file",
|
||||||
|
|
@ -3461,31 +3462,60 @@ fn writeConfigTemplate(path: []const u8) !void {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load configurations from the default configuration files. The default
|
/// Load configurations from the default configuration files. The default
|
||||||
/// configuration file is at `$XDG_CONFIG_HOME/ghostty/config`.
|
/// configuration file is at `$XDG_CONFIG_HOME/ghostty/config.ghostty`.
|
||||||
///
|
///
|
||||||
/// On macOS, `$HOME/Library/Application Support/$CFBundleIdentifier/config`
|
/// On macOS, `$HOME/Library/Application Support/$CFBundleIdentifier/`
|
||||||
/// is also loaded.
|
/// is also loaded.
|
||||||
|
///
|
||||||
|
/// The legacy `config` file (without extension) is first loaded,
|
||||||
|
/// then `config.ghostty`.
|
||||||
pub fn loadDefaultFiles(self: *Config, alloc: Allocator) !void {
|
pub fn loadDefaultFiles(self: *Config, alloc: Allocator) !void {
|
||||||
// Load XDG first
|
// Load XDG first
|
||||||
const xdg_path = try defaultXdgPath(alloc);
|
const legacy_xdg_path = try file_load.legacyDefaultXdgPath(alloc);
|
||||||
|
defer alloc.free(legacy_xdg_path);
|
||||||
|
const xdg_path = try file_load.defaultXdgPath(alloc);
|
||||||
defer alloc.free(xdg_path);
|
defer alloc.free(xdg_path);
|
||||||
const xdg_action = self.loadOptionalFile(alloc, xdg_path);
|
const xdg_loaded: bool = xdg_loaded: {
|
||||||
|
const legacy_xdg_action = self.loadOptionalFile(alloc, legacy_xdg_path);
|
||||||
|
const xdg_action = self.loadOptionalFile(alloc, xdg_path);
|
||||||
|
if (xdg_action != .not_found and legacy_xdg_action != .not_found) {
|
||||||
|
log.warn("both config files `{s}` and `{s}` exist.", .{ legacy_xdg_path, xdg_path });
|
||||||
|
log.warn("loading them both in that order", .{});
|
||||||
|
break :xdg_loaded true;
|
||||||
|
}
|
||||||
|
|
||||||
|
break :xdg_loaded xdg_action != .not_found or
|
||||||
|
legacy_xdg_action != .not_found;
|
||||||
|
};
|
||||||
|
|
||||||
// On macOS load the app support directory as well
|
// On macOS load the app support directory as well
|
||||||
if (comptime builtin.os.tag == .macos) {
|
if (comptime builtin.os.tag == .macos) {
|
||||||
const app_support_path = try defaultAppSupportPath(alloc);
|
const legacy_app_support_path = try file_load.legacyDefaultAppSupportPath(alloc);
|
||||||
|
defer alloc.free(legacy_app_support_path);
|
||||||
|
const app_support_path = try file_load.preferredAppSupportPath(alloc);
|
||||||
defer alloc.free(app_support_path);
|
defer alloc.free(app_support_path);
|
||||||
const app_support_action = self.loadOptionalFile(alloc, app_support_path);
|
const app_support_loaded: bool = loaded: {
|
||||||
|
const legacy_app_support_action = self.loadOptionalFile(alloc, legacy_app_support_path);
|
||||||
|
const app_support_action = self.loadOptionalFile(alloc, app_support_path);
|
||||||
|
if (app_support_action != .not_found and legacy_app_support_action != .not_found) {
|
||||||
|
log.warn("both config files `{s}` and `{s}` exist.", .{ legacy_app_support_path, app_support_path });
|
||||||
|
log.warn("loading them both in that order", .{});
|
||||||
|
break :loaded true;
|
||||||
|
}
|
||||||
|
|
||||||
|
break :loaded app_support_action != .not_found or
|
||||||
|
legacy_app_support_action != .not_found;
|
||||||
|
};
|
||||||
|
|
||||||
// If both files are not found, then we create a template file.
|
// If both files are not found, then we create a template file.
|
||||||
// For macOS, we only create the template file in the app support
|
// For macOS, we only create the template file in the app support
|
||||||
if (app_support_action == .not_found and xdg_action == .not_found) {
|
if (app_support_loaded and xdg_loaded) {
|
||||||
writeConfigTemplate(app_support_path) catch |err| {
|
writeConfigTemplate(app_support_path) catch |err| {
|
||||||
log.warn("error creating template config file err={}", .{err});
|
log.warn("error creating template config file err={}", .{err});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (xdg_action == .not_found) {
|
if (xdg_loaded) {
|
||||||
writeConfigTemplate(xdg_path) catch |err| {
|
writeConfigTemplate(xdg_path) catch |err| {
|
||||||
log.warn("error creating template config file err={}", .{err});
|
log.warn("error creating template config file err={}", .{err});
|
||||||
};
|
};
|
||||||
|
|
@ -3493,102 +3523,6 @@ pub fn loadDefaultFiles(self: *Config, alloc: Allocator) !void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Default path for the XDG home configuration file. Returned value
|
|
||||||
/// must be freed by the caller.
|
|
||||||
fn defaultXdgPath(alloc: Allocator) ![]const u8 {
|
|
||||||
return try internal_os.xdg.config(
|
|
||||||
alloc,
|
|
||||||
.{ .subdir = "ghostty/config" },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Default path for the macOS Application Support configuration file.
|
|
||||||
/// Returned value must be freed by the caller.
|
|
||||||
fn defaultAppSupportPath(alloc: Allocator) ![]const u8 {
|
|
||||||
return try internal_os.macos.appSupportDir(alloc, "config");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the path to the preferred default configuration file.
|
|
||||||
/// This is the file where users should place their configuration.
|
|
||||||
///
|
|
||||||
/// This doesn't create or populate the file with any default
|
|
||||||
/// contents; downstream callers must handle this.
|
|
||||||
///
|
|
||||||
/// The returned value must be freed by the caller.
|
|
||||||
pub fn preferredDefaultFilePath(alloc: Allocator) ![]const u8 {
|
|
||||||
switch (builtin.os.tag) {
|
|
||||||
.macos => {
|
|
||||||
// macOS prefers the Application Support directory
|
|
||||||
// if it exists.
|
|
||||||
const app_support_path = try defaultAppSupportPath(alloc);
|
|
||||||
if (openFile(app_support_path)) |f| {
|
|
||||||
f.close();
|
|
||||||
return app_support_path;
|
|
||||||
} else |_| {}
|
|
||||||
|
|
||||||
// Try the XDG path if it exists
|
|
||||||
const xdg_path = try defaultXdgPath(alloc);
|
|
||||||
if (openFile(xdg_path)) |f| {
|
|
||||||
f.close();
|
|
||||||
alloc.free(app_support_path);
|
|
||||||
return xdg_path;
|
|
||||||
} else |_| {}
|
|
||||||
defer alloc.free(xdg_path);
|
|
||||||
|
|
||||||
// Neither exist, use app support
|
|
||||||
return app_support_path;
|
|
||||||
},
|
|
||||||
|
|
||||||
// All other platforms use XDG only
|
|
||||||
else => return try defaultXdgPath(alloc),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const OpenFileError = error{
|
|
||||||
FileNotFound,
|
|
||||||
FileIsEmpty,
|
|
||||||
FileOpenFailed,
|
|
||||||
NotAFile,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Opens the file at the given path and returns the file handle
|
|
||||||
/// if it exists and is non-empty. This also constrains the possible
|
|
||||||
/// errors to a smaller set that we can explicitly handle.
|
|
||||||
fn openFile(path: []const u8) OpenFileError!std.fs.File {
|
|
||||||
assert(std.fs.path.isAbsolute(path));
|
|
||||||
|
|
||||||
var file = std.fs.openFileAbsolute(
|
|
||||||
path,
|
|
||||||
.{},
|
|
||||||
) catch |err| switch (err) {
|
|
||||||
error.FileNotFound => return OpenFileError.FileNotFound,
|
|
||||||
else => {
|
|
||||||
log.warn("unexpected file open error path={s} err={}", .{
|
|
||||||
path,
|
|
||||||
err,
|
|
||||||
});
|
|
||||||
return OpenFileError.FileOpenFailed;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
errdefer file.close();
|
|
||||||
|
|
||||||
const stat = file.stat() catch |err| {
|
|
||||||
log.warn("error getting file stat path={s} err={}", .{
|
|
||||||
path,
|
|
||||||
err,
|
|
||||||
});
|
|
||||||
return OpenFileError.FileOpenFailed;
|
|
||||||
};
|
|
||||||
switch (stat.kind) {
|
|
||||||
.file => {},
|
|
||||||
else => return OpenFileError.NotAFile,
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stat.size == 0) return OpenFileError.FileIsEmpty;
|
|
||||||
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Load and parse the CLI args.
|
/// Load and parse the CLI args.
|
||||||
pub fn loadCliArgs(self: *Config, alloc_gpa: Allocator) !void {
|
pub fn loadCliArgs(self: *Config, alloc_gpa: Allocator) !void {
|
||||||
switch (builtin.os.tag) {
|
switch (builtin.os.tag) {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ const assert = std.debug.assert;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const ArenaAllocator = std.heap.ArenaAllocator;
|
const ArenaAllocator = std.heap.ArenaAllocator;
|
||||||
const internal_os = @import("../os/main.zig");
|
const internal_os = @import("../os/main.zig");
|
||||||
|
const file_load = @import("file_load.zig");
|
||||||
|
|
||||||
/// The path to the configuration that should be opened for editing.
|
/// The path to the configuration that should be opened for editing.
|
||||||
///
|
///
|
||||||
|
|
@ -89,20 +90,16 @@ fn configPath(alloc_arena: Allocator) ![]const u8 {
|
||||||
/// Returns a const list of possible paths the main config file could be
|
/// Returns a const list of possible paths the main config file could be
|
||||||
/// in for the current OS.
|
/// in for the current OS.
|
||||||
fn configPathCandidates(alloc_arena: Allocator) ![]const []const u8 {
|
fn configPathCandidates(alloc_arena: Allocator) ![]const []const u8 {
|
||||||
var paths: std.ArrayList([]const u8) = try .initCapacity(alloc_arena, 2);
|
var paths: std.ArrayList([]const u8) = try .initCapacity(alloc_arena, 4);
|
||||||
errdefer paths.deinit(alloc_arena);
|
errdefer paths.deinit(alloc_arena);
|
||||||
|
|
||||||
if (comptime builtin.os.tag == .macos) {
|
if (comptime builtin.os.tag == .macos) {
|
||||||
paths.appendAssumeCapacity(try internal_os.macos.appSupportDir(
|
paths.appendAssumeCapacity(try file_load.defaultAppSupportPath(alloc_arena));
|
||||||
alloc_arena,
|
paths.appendAssumeCapacity(try file_load.legacyDefaultAppSupportPath(alloc_arena));
|
||||||
"config",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
paths.appendAssumeCapacity(try internal_os.xdg.config(
|
paths.appendAssumeCapacity(try file_load.defaultXdgPath(alloc_arena));
|
||||||
alloc_arena,
|
paths.appendAssumeCapacity(try file_load.legacyDefaultXdgPath(alloc_arena));
|
||||||
.{ .subdir = "ghostty/config" },
|
|
||||||
));
|
|
||||||
|
|
||||||
return paths.items;
|
return paths.items;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,166 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
const assert = std.debug.assert;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const internal_os = @import("../os/main.zig");
|
||||||
|
|
||||||
|
const log = std.log.scoped(.config);
|
||||||
|
|
||||||
|
/// Default path for the XDG home configuration file. Returned value
|
||||||
|
/// must be freed by the caller.
|
||||||
|
pub fn defaultXdgPath(alloc: Allocator) ![]const u8 {
|
||||||
|
return try internal_os.xdg.config(
|
||||||
|
alloc,
|
||||||
|
.{ .subdir = "ghostty/config.ghostty" },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ghostty <1.3.0 default path for the XDG home configuration file.
|
||||||
|
/// Returned value must be freed by the caller.
|
||||||
|
pub fn legacyDefaultXdgPath(alloc: Allocator) ![]const u8 {
|
||||||
|
return try internal_os.xdg.config(
|
||||||
|
alloc,
|
||||||
|
.{ .subdir = "ghostty/config" },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Preferred default path for the XDG home configuration file.
|
||||||
|
/// Returned value must be freed by the caller.
|
||||||
|
pub fn preferredXdgPath(alloc: Allocator) ![]const u8 {
|
||||||
|
// If the XDG path exists, use that.
|
||||||
|
const xdg_path = try defaultXdgPath(alloc);
|
||||||
|
if (open(xdg_path)) |f| {
|
||||||
|
f.close();
|
||||||
|
return xdg_path;
|
||||||
|
} else |_| {}
|
||||||
|
|
||||||
|
// Try the legacy path
|
||||||
|
errdefer alloc.free(xdg_path);
|
||||||
|
const legacy_xdg_path = try legacyDefaultXdgPath(alloc);
|
||||||
|
if (open(legacy_xdg_path)) |f| {
|
||||||
|
f.close();
|
||||||
|
alloc.free(xdg_path);
|
||||||
|
return legacy_xdg_path;
|
||||||
|
} else |_| {}
|
||||||
|
|
||||||
|
// Legacy path and XDG path both don't exist. Return the
|
||||||
|
// new one.
|
||||||
|
alloc.free(legacy_xdg_path);
|
||||||
|
return xdg_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Default path for the macOS Application Support configuration file.
|
||||||
|
/// Returned value must be freed by the caller.
|
||||||
|
pub fn defaultAppSupportPath(alloc: Allocator) ![]const u8 {
|
||||||
|
return try internal_os.macos.appSupportDir(alloc, "config.ghostty");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ghostty <1.3.0 default path for the macOS Application Support
|
||||||
|
/// configuration file. Returned value must be freed by the caller.
|
||||||
|
pub fn legacyDefaultAppSupportPath(alloc: Allocator) ![]const u8 {
|
||||||
|
return try internal_os.macos.appSupportDir(alloc, "config");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Preferred default path for the macOS Application Support configuration file.
|
||||||
|
/// Returned value must be freed by the caller.
|
||||||
|
pub fn preferredAppSupportPath(alloc: Allocator) ![]const u8 {
|
||||||
|
// If the app support path exists, use that.
|
||||||
|
const app_support_path = try defaultAppSupportPath(alloc);
|
||||||
|
if (open(app_support_path)) |f| {
|
||||||
|
f.close();
|
||||||
|
return app_support_path;
|
||||||
|
} else |_| {}
|
||||||
|
|
||||||
|
// Try the legacy path
|
||||||
|
errdefer alloc.free(app_support_path);
|
||||||
|
const legacy_app_support_path = try legacyDefaultAppSupportPath(alloc);
|
||||||
|
if (open(legacy_app_support_path)) |f| {
|
||||||
|
f.close();
|
||||||
|
alloc.free(app_support_path);
|
||||||
|
return legacy_app_support_path;
|
||||||
|
} else |_| {}
|
||||||
|
|
||||||
|
// Legacy path and app support path both don't exist. Return the
|
||||||
|
// new one.
|
||||||
|
alloc.free(legacy_app_support_path);
|
||||||
|
return app_support_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the path to the preferred default configuration file.
|
||||||
|
/// This is the file where users should place their configuration.
|
||||||
|
///
|
||||||
|
/// This doesn't create or populate the file with any default
|
||||||
|
/// contents; downstream callers must handle this.
|
||||||
|
///
|
||||||
|
/// The returned value must be freed by the caller.
|
||||||
|
pub fn preferredDefaultFilePath(alloc: Allocator) ![]const u8 {
|
||||||
|
switch (builtin.os.tag) {
|
||||||
|
.macos => {
|
||||||
|
// macOS prefers the Application Support directory
|
||||||
|
// if it exists.
|
||||||
|
const app_support_path = try preferredAppSupportPath(alloc);
|
||||||
|
const app_support_file = open(app_support_path) catch {
|
||||||
|
// Try the XDG path if it exists
|
||||||
|
const xdg_path = try preferredXdgPath(alloc);
|
||||||
|
const xdg_file = open(xdg_path) catch {
|
||||||
|
// If neither file exists, use app support
|
||||||
|
alloc.free(xdg_path);
|
||||||
|
return app_support_path;
|
||||||
|
};
|
||||||
|
xdg_file.close();
|
||||||
|
alloc.free(app_support_path);
|
||||||
|
return xdg_path;
|
||||||
|
};
|
||||||
|
app_support_file.close();
|
||||||
|
return app_support_path;
|
||||||
|
},
|
||||||
|
|
||||||
|
// All other platforms use XDG only
|
||||||
|
else => return try preferredXdgPath(alloc),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const OpenFileError = error{
|
||||||
|
FileNotFound,
|
||||||
|
FileIsEmpty,
|
||||||
|
FileOpenFailed,
|
||||||
|
NotAFile,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Opens the file at the given path and returns the file handle
|
||||||
|
/// if it exists and is non-empty. This also constrains the possible
|
||||||
|
/// errors to a smaller set that we can explicitly handle.
|
||||||
|
pub fn open(path: []const u8) OpenFileError!std.fs.File {
|
||||||
|
assert(std.fs.path.isAbsolute(path));
|
||||||
|
|
||||||
|
var file = std.fs.openFileAbsolute(
|
||||||
|
path,
|
||||||
|
.{},
|
||||||
|
) catch |err| switch (err) {
|
||||||
|
error.FileNotFound => return OpenFileError.FileNotFound,
|
||||||
|
else => {
|
||||||
|
log.warn("unexpected file open error path={s} err={}", .{
|
||||||
|
path,
|
||||||
|
err,
|
||||||
|
});
|
||||||
|
return OpenFileError.FileOpenFailed;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
errdefer file.close();
|
||||||
|
|
||||||
|
const stat = file.stat() catch |err| {
|
||||||
|
log.warn("error getting file stat path={s} err={}", .{
|
||||||
|
path,
|
||||||
|
err,
|
||||||
|
});
|
||||||
|
return OpenFileError.FileOpenFailed;
|
||||||
|
};
|
||||||
|
switch (stat.kind) {
|
||||||
|
.file => {},
|
||||||
|
else => return OpenFileError.NotAFile,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stat.size == 0) return OpenFileError.FileIsEmpty;
|
||||||
|
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
@ -10,7 +10,7 @@ pub const ftdetect =
|
||||||
\\"
|
\\"
|
||||||
\\" THIS FILE IS AUTO-GENERATED
|
\\" THIS FILE IS AUTO-GENERATED
|
||||||
\\
|
\\
|
||||||
\\au BufRead,BufNewFile */ghostty/config,*/ghostty/themes/* setf ghostty
|
\\au BufRead,BufNewFile */ghostty/config,*/ghostty/themes/*,*.ghostty setf ghostty
|
||||||
\\
|
\\
|
||||||
;
|
;
|
||||||
pub const ftplugin =
|
pub const ftplugin =
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue