mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-19 05:50:27 +00:00
config: smarter parsing in autoParseStruct
Fixes #8849 Previously, the `parseAutoStruct` function that was used to parse generic structs for the config simply split the input value on commas without taking into account quoting or escapes. This led to problems because it was impossible to include a comma in the value of config entries that were parsed by `parseAutoStruct`. This is particularly problematic because `ghostty +show-config --default` would produce output like the following: ``` command-palette-entry = title:Focus Split: Next,description:Focus the next split, if any.,action:goto_split:next ``` Because the `description` contains a comma, Ghostty is unable to parse this correctly. The value would be split into four parts: ``` title:Focus Split: Next description:Focus the next split if any. action:goto_split:next ``` Instead of three parts: ``` title:Focus Split: Next description:Focus the next split, if any. action:goto_split:next ``` Because `parseAutoStruct` simply looked for commas to split on, no amount of quoting or escaping would allow that to be parsed correctly. This is fixed by (1) introducing a parser that will split the input to `parseAutoStruct` into fields while taking into account quotes and escaping. And (2) changing the `ghostty +show-config` output to put the values in `command-palette-entry` into quotes so that Ghostty can parse it's own output. `parseAutoStruct` will also now parse double quoted values as a Zig string literal. This makes it easier to embed control codes, whitespace, and commas in values.
This commit is contained in:
@@ -7,6 +7,7 @@ const diags = @import("diagnostics.zig");
|
||||
const internal_os = @import("../os/main.zig");
|
||||
const Diagnostic = diags.Diagnostic;
|
||||
const DiagnosticList = diags.DiagnosticList;
|
||||
const Splitter = @import("Splitter.zig");
|
||||
|
||||
const log = std.log.scoped(.cli);
|
||||
|
||||
@@ -527,24 +528,31 @@ pub fn parseAutoStruct(comptime T: type, alloc: Allocator, v: []const u8) !T {
|
||||
const FieldSet = std.StaticBitSet(info.fields.len);
|
||||
var fields_set: FieldSet = .initEmpty();
|
||||
|
||||
// We split each value by ","
|
||||
var iter = std.mem.splitSequence(u8, v, ",");
|
||||
loop: while (iter.next()) |entry| {
|
||||
// We split each value by "," allowing for quoting and escaping.
|
||||
var iter: Splitter = .init(v);
|
||||
loop: while (try iter.next()) |entry| {
|
||||
// Find the key/value, trimming whitespace. The value may be quoted
|
||||
// which we strip the quotes from.
|
||||
const idx = mem.indexOf(u8, entry, ":") orelse return error.InvalidValue;
|
||||
const key = std.mem.trim(u8, entry[0..idx], whitespace);
|
||||
|
||||
// used if we need to decode a double-quoted string.
|
||||
var buf: std.ArrayListUnmanaged(u8) = .empty;
|
||||
defer buf.deinit(alloc);
|
||||
|
||||
const value = value: {
|
||||
var value = std.mem.trim(u8, entry[idx + 1 ..], whitespace);
|
||||
const value = std.mem.trim(u8, entry[idx + 1 ..], whitespace);
|
||||
|
||||
// Detect a quoted string.
|
||||
if (value.len >= 2 and
|
||||
value[0] == '"' and
|
||||
value[value.len - 1] == '"')
|
||||
{
|
||||
// Trim quotes since our CLI args processor expects
|
||||
// quotes to already be gone.
|
||||
value = value[1 .. value.len - 1];
|
||||
// Decode a double-quoted string as a Zig string literal.
|
||||
const writer = buf.writer(alloc);
|
||||
const parsed = try std.zig.string_literal.parseWrite(writer, value);
|
||||
if (parsed == .failure) return error.InvalidValue;
|
||||
break :value buf.items;
|
||||
}
|
||||
|
||||
break :value value;
|
||||
|
||||
Reference in New Issue
Block a user