mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-05-20 20:04:26 +00:00
config: richer diagnostics for errors
Rather than storing a list of errors we now store a list of "diagnostics." Each diagnostic has a richer set of structured information, including a message, a key, the location where it occurred. This lets us show more detailed messages, more human friendly messages, and also let's us filter by key or location. We don't take advantage of all of this capability in this initial commit, but we do use every field for something.
This commit is contained in:
@@ -112,14 +112,15 @@ fn config_trigger_(
|
||||
return trigger.cval();
|
||||
}
|
||||
|
||||
export fn ghostty_config_errors_count(self: *Config) u32 {
|
||||
return @intCast(self._errors.list.items.len);
|
||||
export fn ghostty_config_diagnostics_count(self: *Config) u32 {
|
||||
return @intCast(self._diagnostics.items().len);
|
||||
}
|
||||
|
||||
export fn ghostty_config_get_error(self: *Config, idx: u32) Error {
|
||||
if (idx >= self._errors.list.items.len) return .{};
|
||||
const err = self._errors.list.items[idx];
|
||||
return .{ .message = err.message.ptr };
|
||||
export fn ghostty_config_get_diagnostic(self: *Config, idx: u32) Diagnostic {
|
||||
const items = self._diagnostics.items();
|
||||
if (idx >= items.len) return .{};
|
||||
const message = self._diagnostics.precompute.messages.items[idx];
|
||||
return .{ .message = message.ptr };
|
||||
}
|
||||
|
||||
export fn ghostty_config_open() void {
|
||||
@@ -128,7 +129,7 @@ export fn ghostty_config_open() void {
|
||||
};
|
||||
}
|
||||
|
||||
/// Sync with ghostty_error_s
|
||||
const Error = extern struct {
|
||||
/// Sync with ghostty_diagnostic_s
|
||||
const Diagnostic = extern struct {
|
||||
message: [*:0]const u8 = "",
|
||||
};
|
||||
|
||||
@@ -1662,10 +1662,9 @@ term: []const u8 = "xterm-ghostty",
|
||||
/// This is set by the CLI parser for deinit.
|
||||
_arena: ?ArenaAllocator = null,
|
||||
|
||||
/// List of errors that occurred while loading. This can be accessed directly
|
||||
/// by callers. It is only underscore-prefixed so it can't be set by the
|
||||
/// configuration file.
|
||||
_errors: ErrorList = .{},
|
||||
/// List of diagnostics that were generated during the loading of
|
||||
/// the configuration.
|
||||
_diagnostics: cli.DiagnosticList = .{},
|
||||
|
||||
/// The steps we can use to reload the configuration after it has been loaded
|
||||
/// without reopening the files. This is used in very specific cases such
|
||||
@@ -2446,7 +2445,7 @@ 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._errors.add(arena_alloc, .{
|
||||
try self._diagnostics.append(arena_alloc, .{
|
||||
.message = try std.fmt.allocPrintZ(
|
||||
arena_alloc,
|
||||
"config-file {s}: cycle detected",
|
||||
@@ -2458,7 +2457,7 @@ pub fn loadRecursiveFiles(self: *Config, alloc_gpa: Allocator) !void {
|
||||
|
||||
var file = cwd.openFile(path, .{}) catch |err| {
|
||||
if (err != error.FileNotFound or !optional) {
|
||||
try self._errors.add(arena_alloc, .{
|
||||
try self._diagnostics.append(arena_alloc, .{
|
||||
.message = try std.fmt.allocPrintZ(
|
||||
arena_alloc,
|
||||
"error opening config-file {s}: {}",
|
||||
@@ -2495,7 +2494,7 @@ fn expandPaths(self: *Config, base: []const u8) !void {
|
||||
try @field(self, field.name).expand(
|
||||
arena_alloc,
|
||||
base,
|
||||
&self._errors,
|
||||
&self._diagnostics,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2506,7 +2505,7 @@ fn loadTheme(self: *Config, theme: []const u8) !void {
|
||||
const file: std.fs.File = (try themepkg.open(
|
||||
self._arena.?.allocator(),
|
||||
theme,
|
||||
&self._errors,
|
||||
&self._diagnostics,
|
||||
)) orelse return;
|
||||
defer file.close();
|
||||
|
||||
@@ -2697,7 +2696,12 @@ pub fn finalize(self: *Config) !void {
|
||||
|
||||
/// Callback for src/cli/args.zig to allow us to handle special cases
|
||||
/// like `--help` or `-e`. Returns "false" if the CLI parsing should halt.
|
||||
pub fn parseManuallyHook(self: *Config, alloc: Allocator, arg: []const u8, iter: anytype) !bool {
|
||||
pub fn parseManuallyHook(
|
||||
self: *Config,
|
||||
alloc: Allocator,
|
||||
arg: []const u8,
|
||||
iter: anytype,
|
||||
) !bool {
|
||||
// Keep track of our input args no matter what..
|
||||
try self._replay_steps.append(alloc, .{ .arg = try alloc.dupe(u8, arg) });
|
||||
|
||||
@@ -2714,7 +2718,8 @@ pub fn parseManuallyHook(self: *Config, alloc: Allocator, arg: []const u8, iter:
|
||||
}
|
||||
|
||||
if (command.items.len == 0) {
|
||||
try self._errors.add(alloc, .{
|
||||
try self._diagnostics.append(alloc, .{
|
||||
.location = cli.Diagnostic.Location.fromIter(iter),
|
||||
.message = try std.fmt.allocPrintZ(
|
||||
alloc,
|
||||
"missing command after {s}",
|
||||
@@ -3506,7 +3511,7 @@ pub const RepeatablePath = struct {
|
||||
self: *Self,
|
||||
alloc: Allocator,
|
||||
base: []const u8,
|
||||
errors: *ErrorList,
|
||||
diags: *cli.DiagnosticList,
|
||||
) !void {
|
||||
assert(std.fs.path.isAbsolute(base));
|
||||
var dir = try std.fs.cwd().openDir(base, .{});
|
||||
@@ -3533,7 +3538,7 @@ pub const RepeatablePath = struct {
|
||||
break :abs buf[0..resolved.len];
|
||||
}
|
||||
|
||||
try errors.add(alloc, .{
|
||||
try diags.append(alloc, .{
|
||||
.message = try std.fmt.allocPrintZ(
|
||||
alloc,
|
||||
"error resolving file path {s}: {}",
|
||||
|
||||
@@ -4,7 +4,7 @@ const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const global_state = &@import("../global.zig").state;
|
||||
const internal_os = @import("../os/main.zig");
|
||||
const ErrorList = @import("ErrorList.zig");
|
||||
const cli = @import("../cli.zig");
|
||||
|
||||
/// Location of possible themes. The order of this enum matters because it
|
||||
/// defines the priority of theme search (from top to bottom).
|
||||
@@ -107,19 +107,19 @@ pub const LocationIterator = struct {
|
||||
pub fn open(
|
||||
arena_alloc: Allocator,
|
||||
theme: []const u8,
|
||||
errors: *ErrorList,
|
||||
diags: *cli.DiagnosticList,
|
||||
) error{OutOfMemory}!?std.fs.File {
|
||||
|
||||
// Absolute themes are loaded a different path.
|
||||
if (std.fs.path.isAbsolute(theme)) return try openAbsolute(
|
||||
arena_alloc,
|
||||
theme,
|
||||
errors,
|
||||
diags,
|
||||
);
|
||||
|
||||
const basename = std.fs.path.basename(theme);
|
||||
if (!std.mem.eql(u8, theme, basename)) {
|
||||
try errors.add(arena_alloc, .{
|
||||
try diags.append(arena_alloc, .{
|
||||
.message = try std.fmt.allocPrintZ(
|
||||
arena_alloc,
|
||||
"theme \"{s}\" cannot include path separators unless it is an absolute path",
|
||||
@@ -143,7 +143,7 @@ pub fn open(
|
||||
|
||||
// Anything else is an error we log and give up on.
|
||||
else => {
|
||||
try errors.add(arena_alloc, .{
|
||||
try diags.append(arena_alloc, .{
|
||||
.message = try std.fmt.allocPrintZ(
|
||||
arena_alloc,
|
||||
"failed to load theme \"{s}\" from the file \"{s}\": {}",
|
||||
@@ -163,7 +163,7 @@ pub fn open(
|
||||
it.reset();
|
||||
while (try it.next()) |loc| {
|
||||
const path = try std.fs.path.join(arena_alloc, &.{ loc.dir, theme });
|
||||
try errors.add(arena_alloc, .{
|
||||
try diags.append(arena_alloc, .{
|
||||
.message = try std.fmt.allocPrintZ(
|
||||
arena_alloc,
|
||||
"theme \"{s}\" not found, tried path \"{s}\"",
|
||||
@@ -186,18 +186,18 @@ pub fn open(
|
||||
pub fn openAbsolute(
|
||||
arena_alloc: Allocator,
|
||||
theme: []const u8,
|
||||
errors: *ErrorList,
|
||||
diags: *cli.DiagnosticList,
|
||||
) error{OutOfMemory}!?std.fs.File {
|
||||
return std.fs.openFileAbsolute(theme, .{}) catch |err| {
|
||||
switch (err) {
|
||||
error.FileNotFound => try errors.add(arena_alloc, .{
|
||||
error.FileNotFound => try diags.append(arena_alloc, .{
|
||||
.message = try std.fmt.allocPrintZ(
|
||||
arena_alloc,
|
||||
"failed to load theme from the path \"{s}\"",
|
||||
.{theme},
|
||||
),
|
||||
}),
|
||||
else => try errors.add(arena_alloc, .{
|
||||
else => try diags.append(arena_alloc, .{
|
||||
.message = try std.fmt.allocPrintZ(
|
||||
arena_alloc,
|
||||
"failed to load theme from the path \"{s}\": {}",
|
||||
|
||||
Reference in New Issue
Block a user