1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
const std = @import("std");
const Config = @import("../../config/Config.zig");
const help_strings = @import("help_strings");
pub fn main(init: std.process.Init) !void {
var buffer: [2048]u8 = undefined;
var stdout_writer = std.Io.File.stdout().writer(init.io, &buffer);
const stdout = &stdout_writer.interface;
try genConfig(stdout);
}
pub fn genConfig(writer: *std.Io.Writer) !void {
// Write the header
try writer.writeAll(
\\---
\\title: Reference
\\description: Reference of all Ghostty configuration options.
\\editOnGithubLink: https://github.com/ghostty-org/ghostty/edit/main/src/config/Config.zig
\\---
\\
\\This is a reference of all Ghostty configuration options. These
\\options are ordered roughly by how common they are to be used
\\and grouped with related options. I recommend utilizing your
\\browser's search functionality to find the option you're looking
\\for.
\\
\\In the future, we'll have a more user-friendly way to view and
\\organize these options.
\\
\\
);
@setEvalBranchQuota(50_000);
const fields = @typeInfo(Config).@"struct".fields;
inline for (fields, 0..) |field, i| {
if (field.name[0] == '_') continue;
if (!@hasDecl(help_strings.Config, field.name)) continue;
// Write the field name.
try writer.writeAll("## `");
try writer.writeAll(field.name);
try writer.writeAll("`\n");
// For all subsequent fields with no docs, they are grouped
// with the previous field.
if (i + 1 < fields.len) {
inline for (fields[i + 1 ..]) |next_field| {
if (next_field.name[0] == '_') break;
if (@hasDecl(help_strings.Config, next_field.name)) break;
try writer.writeAll("## `");
try writer.writeAll(next_field.name);
try writer.writeAll("`\n");
}
}
// Newline after our headers
try writer.writeAll("\n");
var iter = std.mem.splitScalar(
u8,
@field(help_strings.Config, field.name),
'\n',
);
// We do some really rough markdown "parsing" here so that
// we can fix up some styles for what our website expects.
var block: ?enum {
/// Plaintext, do nothing.
text,
/// Code block, wrap in triple backticks. We use indented
/// code blocks in our comments but the website parser only
/// supports triple backticks.
code,
/// Callouts. We detect these based on paragraphs starting
/// with "Note:", "Warning:", etc. (case-insensitive).
callout_note,
callout_warning,
} = null;
while (iter.next()) |s| {
// Empty line resets our block
if (std.mem.eql(u8, s, "")) {
try endBlock(writer, block);
block = null;
try writer.writeAll("\n");
continue;
}
// If we don't have a block figure out our type.
const first: bool = block == null;
if (block == null) {
if (std.mem.startsWith(u8, s, " ")) {
block = .code;
try writer.writeAll("```\n");
} else if (std.ascii.startsWithIgnoreCase(s, "note:")) {
block = .callout_note;
try writer.writeAll("<Note>\n");
} else if (std.ascii.startsWithIgnoreCase(s, "warning:")) {
block = .callout_warning;
try writer.writeAll("<Warning>\n");
} else {
block = .text;
}
}
try writer.writeAll(switch (block.?) {
.text => s,
.callout_note => if (first) s["note:".len..] else s,
.callout_warning => if (first) s["warning:".len..] else s,
.code => if (std.mem.startsWith(u8, s, " "))
s[4..]
else
s,
});
try writer.writeAll("\n");
}
try endBlock(writer, block);
try writer.writeAll("\n");
}
}
fn endBlock(writer: *std.Io.Writer, block: anytype) !void {
if (block) |v| switch (v) {
.text => {},
.code => try writer.writeAll("```\n"),
.callout_note => try writer.writeAll("</Note>\n"),
.callout_warning => try writer.writeAll("</Warning>\n"),
};
}
|