mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 16:56:09 +03:00
config: ability to format all field types except tagged unions
This commit is contained in:
@ -2178,6 +2178,19 @@ pub const Color = packed struct(u24) {
|
|||||||
return std.meta.eql(self, other);
|
return std.meta.eql(self, other);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used by Formatter
|
||||||
|
pub fn formatEntry(self: Color, formatter: anytype) !void {
|
||||||
|
var buf: [128]u8 = undefined;
|
||||||
|
try formatter.formatEntry(
|
||||||
|
[]const u8,
|
||||||
|
std.fmt.bufPrint(
|
||||||
|
&buf,
|
||||||
|
"#{x:0>2}{x:0>2}{x:0>2}",
|
||||||
|
.{ self.r, self.g, self.b },
|
||||||
|
) catch return error.OutOfMemory,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// fromHex parses a color from a hex value such as #RRGGBB. The "#"
|
/// fromHex parses a color from a hex value such as #RRGGBB. The "#"
|
||||||
/// is optional.
|
/// is optional.
|
||||||
pub fn fromHex(input: []const u8) !Color {
|
pub fn fromHex(input: []const u8) !Color {
|
||||||
@ -2251,6 +2264,21 @@ pub const Palette = struct {
|
|||||||
return std.meta.eql(self, other);
|
return std.meta.eql(self, other);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used by Formatter
|
||||||
|
pub fn formatEntry(self: Self, formatter: anytype) !void {
|
||||||
|
var buf: [128]u8 = undefined;
|
||||||
|
for (0.., self.value) |k, v| {
|
||||||
|
try formatter.formatEntry(
|
||||||
|
[]const u8,
|
||||||
|
std.fmt.bufPrint(
|
||||||
|
&buf,
|
||||||
|
"{d}=#{x:0>2}{x:0>2}{x:0>2}",
|
||||||
|
.{ k, v.r, v.g, v.b },
|
||||||
|
) catch return error.OutOfMemory,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test "parseCLI" {
|
test "parseCLI" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
|
|
||||||
@ -2314,6 +2342,19 @@ pub const RepeatableString = struct {
|
|||||||
} else return true;
|
} else return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used by Formatter
|
||||||
|
pub fn formatEntry(self: Self, formatter: anytype) !void {
|
||||||
|
// If no items, we want to render an empty field.
|
||||||
|
if (self.list.items.len == 0) {
|
||||||
|
try formatter.formatEntry(void, {});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (self.list.items) |value| {
|
||||||
|
try formatter.formatEntry([]const u8, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test "parseCLI" {
|
test "parseCLI" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
var arena = ArenaAllocator.init(testing.allocator);
|
var arena = ArenaAllocator.init(testing.allocator);
|
||||||
@ -2355,6 +2396,11 @@ pub const RepeatablePath = struct {
|
|||||||
return self.value.equal(other.value);
|
return self.value.equal(other.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used by Formatter
|
||||||
|
pub fn formatEntry(self: Self, formatter: anytype) !void {
|
||||||
|
try self.value.formatEntry(formatter);
|
||||||
|
}
|
||||||
|
|
||||||
/// Expand all the paths relative to the base directory.
|
/// Expand all the paths relative to the base directory.
|
||||||
pub fn expand(
|
pub fn expand(
|
||||||
self: *Self,
|
self: *Self,
|
||||||
@ -2442,6 +2488,26 @@ pub const RepeatableFontVariation = struct {
|
|||||||
} else return true;
|
} else return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used by Formatter
|
||||||
|
pub fn formatEntry(
|
||||||
|
self: Self,
|
||||||
|
formatter: anytype,
|
||||||
|
) !void {
|
||||||
|
if (self.list.items.len == 0) {
|
||||||
|
try formatter.formatEntry(void, {});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf: [128]u8 = undefined;
|
||||||
|
for (self.list.items) |value| {
|
||||||
|
const str = std.fmt.bufPrint(&buf, "{s}={d}", .{
|
||||||
|
value.id.str(),
|
||||||
|
value.value,
|
||||||
|
}) catch return error.OutOfMemory;
|
||||||
|
try formatter.formatEntry([]const u8, str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test "parseCLI" {
|
test "parseCLI" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
var arena = ArenaAllocator.init(testing.allocator);
|
var arena = ArenaAllocator.init(testing.allocator);
|
||||||
@ -2561,6 +2627,29 @@ pub const Keybinds = struct {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used by Formatter
|
||||||
|
pub fn formatEntry(self: Keybinds, formatter: anytype) !void {
|
||||||
|
if (self.set.bindings.size == 0) {
|
||||||
|
try formatter.formatEntry(void, {});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf: [1024]u8 = undefined;
|
||||||
|
var iter = self.set.bindings.iterator();
|
||||||
|
while (iter.next()) |next| {
|
||||||
|
const k = next.key_ptr.*;
|
||||||
|
const v = next.value_ptr.*;
|
||||||
|
try formatter.formatEntry(
|
||||||
|
[]const u8,
|
||||||
|
std.fmt.bufPrint(
|
||||||
|
&buf,
|
||||||
|
"{}={}",
|
||||||
|
.{ k, v },
|
||||||
|
) catch return error.OutOfMemory,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test "parseCLI" {
|
test "parseCLI" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
var arena = ArenaAllocator.init(testing.allocator);
|
var arena = ArenaAllocator.init(testing.allocator);
|
||||||
@ -2618,6 +2707,49 @@ pub const RepeatableCodepointMap = struct {
|
|||||||
} else return true;
|
} else return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used by Formatter
|
||||||
|
pub fn formatEntry(
|
||||||
|
self: Self,
|
||||||
|
formatter: anytype,
|
||||||
|
) !void {
|
||||||
|
if (self.map.list.len == 0) {
|
||||||
|
try formatter.formatEntry(void, {});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf: [1024]u8 = undefined;
|
||||||
|
const ranges = self.map.list.items(.range);
|
||||||
|
const descriptors = self.map.list.items(.descriptor);
|
||||||
|
for (ranges, descriptors) |range, descriptor| {
|
||||||
|
if (range[0] == range[1]) {
|
||||||
|
try formatter.formatEntry(
|
||||||
|
[]const u8,
|
||||||
|
std.fmt.bufPrint(
|
||||||
|
&buf,
|
||||||
|
"U+{X:0>4}={s}",
|
||||||
|
.{
|
||||||
|
range[0],
|
||||||
|
descriptor.family orelse "",
|
||||||
|
},
|
||||||
|
) catch return error.OutOfMemory,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
try formatter.formatEntry(
|
||||||
|
[]const u8,
|
||||||
|
std.fmt.bufPrint(
|
||||||
|
&buf,
|
||||||
|
"U+{X:0>4}-U{X:0>4}={s}",
|
||||||
|
.{
|
||||||
|
range[0],
|
||||||
|
range[1],
|
||||||
|
descriptor.family orelse "",
|
||||||
|
},
|
||||||
|
) catch return error.OutOfMemory,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Parses the list of Unicode codepoint ranges. Valid syntax:
|
/// Parses the list of Unicode codepoint ranges. Valid syntax:
|
||||||
///
|
///
|
||||||
/// "" (empty returns null)
|
/// "" (empty returns null)
|
||||||
@ -2836,6 +2968,13 @@ pub const RepeatableLink = struct {
|
|||||||
_ = other;
|
_ = other;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used by Formatter
|
||||||
|
pub fn formatEntry(self: Self, formatter: anytype) !void {
|
||||||
|
// This currently can't be set so we don't format anything.
|
||||||
|
_ = self;
|
||||||
|
_ = formatter;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Options for copy on select behavior.
|
/// Options for copy on select behavior.
|
||||||
|
@ -19,7 +19,7 @@ pub const FileFormatter = struct {
|
|||||||
|
|
||||||
inline for (@typeInfo(Config).Struct.fields) |field| {
|
inline for (@typeInfo(Config).Struct.fields) |field| {
|
||||||
if (field.name[0] == '_') continue;
|
if (field.name[0] == '_') continue;
|
||||||
try self.formatField(
|
try self.formatEntry(
|
||||||
field.type,
|
field.type,
|
||||||
field.name,
|
field.name,
|
||||||
@field(self.config, field.name),
|
@field(self.config, field.name),
|
||||||
@ -28,13 +28,32 @@ pub const FileFormatter = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn formatField(
|
pub fn formatEntry(
|
||||||
self: FileFormatter,
|
self: FileFormatter,
|
||||||
comptime T: type,
|
comptime T: type,
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
value: T,
|
value: T,
|
||||||
writer: anytype,
|
writer: anytype,
|
||||||
) !void {
|
) !void {
|
||||||
|
const EntryFormatter = struct {
|
||||||
|
parent: *const FileFormatter,
|
||||||
|
name: []const u8,
|
||||||
|
writer: @TypeOf(writer),
|
||||||
|
|
||||||
|
pub fn formatEntry(
|
||||||
|
self_entry: @This(),
|
||||||
|
comptime EntryT: type,
|
||||||
|
value_entry: EntryT,
|
||||||
|
) !void {
|
||||||
|
return self_entry.parent.formatEntry(
|
||||||
|
EntryT,
|
||||||
|
self_entry.name,
|
||||||
|
value_entry,
|
||||||
|
self_entry.writer,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
switch (@typeInfo(T)) {
|
switch (@typeInfo(T)) {
|
||||||
.Bool, .Int => {
|
.Bool, .Int => {
|
||||||
try writer.print("{s} = {}\n", .{ name, value });
|
try writer.print("{s} = {}\n", .{ name, value });
|
||||||
@ -46,15 +65,29 @@ pub const FileFormatter = struct {
|
|||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
|
||||||
.Optional => |info| if (value) |inner| {
|
.Enum => {
|
||||||
try self.formatField(
|
try writer.print("{s} = {s}\n", .{ name, @tagName(value) });
|
||||||
info.child,
|
return;
|
||||||
name,
|
},
|
||||||
inner,
|
|
||||||
writer,
|
.Void => {
|
||||||
);
|
|
||||||
} else {
|
|
||||||
try writer.print("{s} = \n", .{name});
|
try writer.print("{s} = \n", .{name});
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
|
.Optional => |info| {
|
||||||
|
if (value) |inner| {
|
||||||
|
try self.formatEntry(
|
||||||
|
info.child,
|
||||||
|
name,
|
||||||
|
inner,
|
||||||
|
writer,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
try writer.print("{s} = \n", .{name});
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
},
|
},
|
||||||
|
|
||||||
.Pointer => switch (T) {
|
.Pointer => switch (T) {
|
||||||
@ -62,16 +95,51 @@ pub const FileFormatter = struct {
|
|||||||
[:0]const u8,
|
[:0]const u8,
|
||||||
=> {
|
=> {
|
||||||
try writer.print("{s} = {s}\n", .{ name, value });
|
try writer.print("{s} = {s}\n", .{ name, value });
|
||||||
|
return;
|
||||||
},
|
},
|
||||||
|
|
||||||
else => {},
|
else => {},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Structs of all types require a "formatEntry" function
|
||||||
|
// to be defined which will be called to format the value.
|
||||||
|
// This is given the formatter in use so that they can
|
||||||
|
// call BACK to our formatEntry to write each primitive
|
||||||
|
// value.
|
||||||
|
.Struct => |info| if (@hasDecl(T, "formatEntry")) {
|
||||||
|
try value.formatEntry(EntryFormatter{
|
||||||
|
.parent = &self,
|
||||||
|
.name = name,
|
||||||
|
.writer = writer,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
} else switch (info.layout) {
|
||||||
|
// Packed structs we special case.
|
||||||
|
.Packed => {
|
||||||
|
try writer.print("{s} = ", .{name});
|
||||||
|
inline for (info.fields, 0..) |field, i| {
|
||||||
|
if (i > 0) try writer.print(",", .{});
|
||||||
|
try writer.print("{s}{s}", .{
|
||||||
|
if (!@field(value, field.name)) "no-" else "",
|
||||||
|
field.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
try writer.print("\n", .{});
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
|
else => {},
|
||||||
|
},
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
.Union => return,
|
||||||
|
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: make a compiler error so we can detect when
|
// Compile error so that we can catch missing cases.
|
||||||
// we don't support a type.
|
@compileLog(T);
|
||||||
|
@compileError("missing case for type");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user