mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 07:46:12 +03:00
config: store non-reproducible diagnostics in replay steps (#4652)
Fixes #4509 Our config has a replay system so that we can make changes and reproduce the configuration as if we were reloading all the files. This is useful because it lets us "reload" the config under various conditions (system theme change, etc.) without risking failures due to world state changing (i.e. config files change or disappear). The replay system assumed that all diagnostics were reproducible, but this is not the case. For example, we don't reload `config-file` so we can't reproduce diagnostics that come from it. This commit adds a new `diagnostic` replay step that can be used to store non-reproducible diagnostics and `config-file` is updated to use it.
This commit is contained in:
@ -3080,25 +3080,31 @@ pub fn loadRecursiveFiles(self: *Config, alloc_gpa: Allocator) !void {
|
||||
|
||||
// We must only load a unique file once
|
||||
if (try loaded.fetchPut(path, {}) != null) {
|
||||
try self._diagnostics.append(arena_alloc, .{
|
||||
const diag: cli.Diagnostic = .{
|
||||
.message = try std.fmt.allocPrintZ(
|
||||
arena_alloc,
|
||||
"config-file {s}: cycle detected",
|
||||
.{path},
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
try self._diagnostics.append(arena_alloc, diag);
|
||||
try self._replay_steps.append(arena_alloc, .{ .diagnostic = diag });
|
||||
continue;
|
||||
}
|
||||
|
||||
var file = std.fs.openFileAbsolute(path, .{}) catch |err| {
|
||||
if (err != error.FileNotFound or !optional) {
|
||||
try self._diagnostics.append(arena_alloc, .{
|
||||
const diag: cli.Diagnostic = .{
|
||||
.message = try std.fmt.allocPrintZ(
|
||||
arena_alloc,
|
||||
"error opening config-file {s}: {}",
|
||||
.{ path, err },
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
try self._diagnostics.append(arena_alloc, diag);
|
||||
try self._replay_steps.append(arena_alloc, .{ .diagnostic = diag });
|
||||
}
|
||||
continue;
|
||||
};
|
||||
@ -3108,13 +3114,16 @@ pub fn loadRecursiveFiles(self: *Config, alloc_gpa: Allocator) !void {
|
||||
switch (stat.kind) {
|
||||
.file => {},
|
||||
else => |kind| {
|
||||
try self._diagnostics.append(arena_alloc, .{
|
||||
const diag: cli.Diagnostic = .{
|
||||
.message = try std.fmt.allocPrintZ(
|
||||
arena_alloc,
|
||||
"config-file {s}: not reading because file type is {s}",
|
||||
.{ path, @tagName(kind) },
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
try self._diagnostics.append(arena_alloc, diag);
|
||||
try self._replay_steps.append(arena_alloc, .{ .diagnostic = diag });
|
||||
continue;
|
||||
},
|
||||
}
|
||||
@ -3264,7 +3273,7 @@ fn loadTheme(self: *Config, theme: Theme) !void {
|
||||
// Setup our replay to be conditional.
|
||||
conditional: for (new_config._replay_steps.items) |*item| {
|
||||
switch (item.*) {
|
||||
.expand => {},
|
||||
.expand, .diagnostic => {},
|
||||
|
||||
// If we see "-e" then we do NOT make the following arguments
|
||||
// conditional since they are supposed to be part of the
|
||||
@ -3816,6 +3825,16 @@ const Replay = struct {
|
||||
arg: []const u8,
|
||||
},
|
||||
|
||||
/// A diagnostic to be added to the new configuration when
|
||||
/// replayed. This should only be used for diagnostics that won't
|
||||
/// be reproduced during playback. For example, `config-file`
|
||||
/// errors are not reloaded so they should be added here.
|
||||
///
|
||||
/// Diagnostics cannot be conditional. They are always present
|
||||
/// even if the conditionals don't match. This helps users find
|
||||
/// errors in their configuration.
|
||||
diagnostic: cli.Diagnostic,
|
||||
|
||||
/// The start of a "-e" argument. This marks the end of
|
||||
/// traditional configuration and the beginning of the
|
||||
/// "-e" initial command magic. This is separate from "arg"
|
||||
@ -3832,6 +3851,7 @@ const Replay = struct {
|
||||
) Allocator.Error!Step {
|
||||
return switch (self) {
|
||||
.@"-e" => self,
|
||||
.diagnostic => |v| .{ .diagnostic = try v.clone(alloc) },
|
||||
.arg => |v| .{ .arg = try alloc.dupe(u8, v) },
|
||||
.expand => |v| .{ .expand = try alloc.dupe(u8, v) },
|
||||
.conditional_arg => |v| conditional: {
|
||||
@ -3867,6 +3887,20 @@ const Replay = struct {
|
||||
log.warn("error expanding paths err={}", .{err});
|
||||
},
|
||||
|
||||
.diagnostic => |diag| diag: {
|
||||
// Best effort to clone and append the diagnostic.
|
||||
// If it fails we log a warning and continue.
|
||||
const arena_alloc = self.config._arena.?.allocator();
|
||||
const cloned = diag.clone(arena_alloc) catch |err| {
|
||||
log.warn("error cloning diagnostic err={}", .{err});
|
||||
break :diag;
|
||||
};
|
||||
self.config._diagnostics.append(arena_alloc, cloned) catch |err| {
|
||||
log.warn("error appending diagnostic err={}", .{err});
|
||||
break :diag;
|
||||
};
|
||||
},
|
||||
|
||||
.conditional_arg => |v| conditional: {
|
||||
// All conditions must match.
|
||||
for (v.conditions) |cond| {
|
||||
|
Reference in New Issue
Block a user