diff --git a/build.zig b/build.zig index 91103e642..08cff1561 100644 --- a/build.zig +++ b/build.zig @@ -153,6 +153,12 @@ pub fn build(b: *std.Build) !void { break :emit_docs path != null; }; + const emit_webdata = b.option( + bool, + "emit-webdata", + "Build the website data for the website.", + ) orelse false; + const emit_xcframework = b.option( bool, "emit-xcframework", @@ -588,6 +594,11 @@ pub fn build(b: *std.Build) !void { b.getInstallStep().dependOn(&b.addInstallFile(placeholder, path).step); } + // Web data + if (emit_webdata) { + try buildWebData(b, config); + } + // App (Linux) if (target.result.os.tag == .linux and config.app_runtime != .none) { // https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html @@ -1578,6 +1589,41 @@ fn buildDocumentation( } } +/// Generate the website reference data that we merge into the +/// official Ghostty website. This isn't meant to be part of any +/// actual build. +fn buildWebData( + b: *std.Build, + config: BuildConfig, +) !void { + const webgen_config = b.addExecutable(.{ + .name = "webgen_config", + .root_source_file = b.path("src/main.zig"), + .target = b.host, + }); + try addHelp(b, webgen_config, config); + + { + const buildconfig = config: { + var copy = config; + copy.exe_entrypoint = .webgen_config; + break :config copy; + }; + + const options = b.addOptions(); + try buildconfig.addOptions(options); + webgen_config.root_module.addOptions("build_options", options); + } + + const webgen_config_step = b.addRunArtifact(webgen_config); + const webgen_config_out = webgen_config_step.captureStdOut(); + + b.getInstallStep().dependOn(&b.addInstallFile( + webgen_config_out, + "share/ghostty/webdata/config.mdx", + ).step); +} + fn benchSteps( b: *std.Build, target: std.Build.ResolvedTarget, diff --git a/src/build/webgen/main_config.zig b/src/build/webgen/main_config.zig new file mode 100644 index 000000000..59eddbca4 --- /dev/null +++ b/src/build/webgen/main_config.zig @@ -0,0 +1,109 @@ +const std = @import("std"); +const Config = @import("../../config/Config.zig"); +const help_strings = @import("help_strings"); + +pub fn main() !void { + const output = std.io.getStdOut().writer(); + try genConfig(output); +} + +pub fn genConfig(writer: anytype) !void { + // Write the header + try writer.writeAll( + \\--- + \\title: Reference + \\description: Reference of all Ghostty configuration options. + \\--- + \\ + \\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(3000); + 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, + } = null; + + while (iter.next()) |s| { + // Empty line resets our block + if (std.mem.eql(u8, s, "")) { + if (block) |v| switch (v) { + .text => {}, + .code => try writer.writeAll("```\n"), + }; + block = null; + + try writer.writeAll("\n"); + continue; + } + + // If we don't have a block figure out our type. + if (block == null) { + if (std.mem.startsWith(u8, s, " ")) { + block = .code; + try writer.writeAll("```\n"); + } else { + block = .text; + } + } + + try writer.writeAll(switch (block.?) { + .text => s, + .code => if (std.mem.startsWith(u8, s, " ")) + s[4..] + else + s, + }); + try writer.writeAll("\n"); + } + try writer.writeAll("\n"); + } +} diff --git a/src/build_config.zig b/src/build_config.zig index 1448f9de5..3bd342897 100644 --- a/src/build_config.zig +++ b/src/build_config.zig @@ -172,6 +172,7 @@ pub const ExeEntrypoint = enum { helpgen, mdgen_ghostty_1, mdgen_ghostty_5, + webgen_config, bench_parser, bench_stream, bench_codepoint_width, diff --git a/src/config/Config.zig b/src/config/Config.zig index bc9277ff4..dde1d069e 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -286,16 +286,16 @@ const c = @cImport({ /// /// Valid values are: /// -/// * `legacy` - Use a legacy method to determine grapheme width, such as -/// wcswidth This maximizes compatibility with legacy programs but may result -/// in incorrect grapheme width for certain graphemes such as skin-tone -/// emoji, non-English characters, etc. +/// * `legacy` - Use a legacy method to determine grapheme width, such as +/// wcswidth This maximizes compatibility with legacy programs but may result +/// in incorrect grapheme width for certain graphemes such as skin-tone +/// emoji, non-English characters, etc. /// -/// This is called "legacy" and not something more specific because the -/// behavior is undefined and we want to retain the ability to modify it. -/// For example, we may or may not use libc `wcswidth` now or in the future. +/// This is called "legacy" and not something more specific because the +/// behavior is undefined and we want to retain the ability to modify it. +/// For example, we may or may not use libc `wcswidth` now or in the future. /// -/// * `unicode` - Use the Unicode standard to determine grapheme width. +/// * `unicode` - Use the Unicode standard to determine grapheme width. /// /// If a running program explicitly enables terminal mode 2027, then `unicode` /// width will be forced regardless of this configuration. When mode 2027 is diff --git a/src/main.zig b/src/main.zig index 895ccfe48..24c5b7a3f 100644 --- a/src/main.zig +++ b/src/main.zig @@ -7,6 +7,7 @@ const entrypoint = switch (build_config.exe_entrypoint) { .helpgen => @import("helpgen.zig"), .mdgen_ghostty_1 => @import("build/mdgen/main_ghostty_1.zig"), .mdgen_ghostty_5 => @import("build/mdgen/main_ghostty_5.zig"), + .webgen_config => @import("build/webgen/main_config.zig"), .bench_parser => @import("bench/parser.zig"), .bench_stream => @import("bench/stream.zig"), .bench_codepoint_width => @import("bench/codepoint-width.zig"),