mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-17 09:16:11 +03:00
Generate documenation (manpages, etc.) from help strings.
This commit is contained in:
94
build.zig
94
build.zig
@ -102,6 +102,12 @@ pub fn build(b: *std.Build) !void {
|
|||||||
"Name of the conformance app to run with 'run' option.",
|
"Name of the conformance app to run with 'run' option.",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
config.documentation = b.option(
|
||||||
|
bool,
|
||||||
|
"documentation",
|
||||||
|
"Build generated documentation",
|
||||||
|
) orelse true;
|
||||||
|
|
||||||
const emit_test_exe = b.option(
|
const emit_test_exe = b.option(
|
||||||
bool,
|
bool,
|
||||||
"emit-test-exe",
|
"emit-test-exe",
|
||||||
@ -414,6 +420,9 @@ pub fn build(b: *std.Build) !void {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Documenation
|
||||||
|
if (config.documentation) buildDocumentation(b, version);
|
||||||
|
|
||||||
// App (Linux)
|
// App (Linux)
|
||||||
if (target.result.os.tag == .linux and config.app_runtime != .none) {
|
if (target.result.os.tag == .linux and config.app_runtime != .none) {
|
||||||
// https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html
|
// https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html
|
||||||
@ -1148,6 +1157,91 @@ fn addHelp(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generate documentation (manpages, etc.) from help strings
|
||||||
|
fn buildDocumentation(
|
||||||
|
b: *std.Build,
|
||||||
|
version: std.SemanticVersion,
|
||||||
|
) void {
|
||||||
|
const manpages = [_]struct {
|
||||||
|
name: []const u8,
|
||||||
|
section: []const u8,
|
||||||
|
}{
|
||||||
|
.{
|
||||||
|
.name = "ghostty",
|
||||||
|
.section = "1",
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.name = "ghostty",
|
||||||
|
.section = "5",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
inline for (manpages) |manpage| {
|
||||||
|
const generate_markdown = b.addExecutable(.{
|
||||||
|
.name = "generate_" ++ manpage.name ++ "_" ++ manpage.section ++ "_markdown",
|
||||||
|
.root_source_file = .{
|
||||||
|
.path = "src/generate_" ++ manpage.name ++ "_" ++ manpage.section ++ "_markdown.zig",
|
||||||
|
},
|
||||||
|
.target = b.host,
|
||||||
|
});
|
||||||
|
|
||||||
|
const generate_markdown_step = b.addRunArtifact(generate_markdown);
|
||||||
|
|
||||||
|
const markdown_output = generate_markdown_step.captureStdOut();
|
||||||
|
|
||||||
|
const generate_markdown_options = b.addOptions();
|
||||||
|
generate_markdown_options.addOption(std.SemanticVersion, "version", version);
|
||||||
|
generate_markdown.root_module.addOptions("build_options", generate_markdown_options);
|
||||||
|
|
||||||
|
addHelp(b, generate_markdown);
|
||||||
|
|
||||||
|
b.getInstallStep().dependOn(&b.addInstallFile(
|
||||||
|
markdown_output,
|
||||||
|
"share/ghostty/doc/" ++ manpage.name ++ "." ++ manpage.section ++ ".md",
|
||||||
|
).step);
|
||||||
|
|
||||||
|
const generate_html = b.addSystemCommand(&.{"pandoc"});
|
||||||
|
generate_html.step.dependOn(&generate_markdown_step.step);
|
||||||
|
generate_html.addArgs(
|
||||||
|
&.{
|
||||||
|
"--standalone",
|
||||||
|
"--from",
|
||||||
|
"markdown",
|
||||||
|
"--to",
|
||||||
|
"html",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
generate_html.addFileArg(markdown_output);
|
||||||
|
|
||||||
|
const html_output = generate_html.captureStdOut();
|
||||||
|
|
||||||
|
b.getInstallStep().dependOn(&b.addInstallFile(
|
||||||
|
html_output,
|
||||||
|
"share/ghostty/doc/" ++ manpage.name ++ "." ++ manpage.section ++ ".html",
|
||||||
|
).step);
|
||||||
|
|
||||||
|
const generate_manpage = b.addSystemCommand(&.{"pandoc"});
|
||||||
|
generate_manpage.step.dependOn(&generate_markdown_step.step);
|
||||||
|
generate_manpage.addArgs(
|
||||||
|
&.{
|
||||||
|
"--standalone",
|
||||||
|
"--from",
|
||||||
|
"markdown",
|
||||||
|
"--to",
|
||||||
|
"man",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
generate_manpage.addFileArg(markdown_output);
|
||||||
|
|
||||||
|
const manpage_output = generate_manpage.captureStdOut();
|
||||||
|
|
||||||
|
b.getInstallStep().dependOn(&b.addInstallFile(
|
||||||
|
manpage_output,
|
||||||
|
"share/man/man" ++ manpage.section ++ "/" ++ manpage.name ++ "." ++ manpage.section,
|
||||||
|
).step);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn benchSteps(
|
fn benchSteps(
|
||||||
b: *std.Build,
|
b: *std.Build,
|
||||||
target: std.Build.ResolvedTarget,
|
target: std.Build.ResolvedTarget,
|
||||||
|
@ -45,6 +45,7 @@
|
|||||||
pixman,
|
pixman,
|
||||||
zlib,
|
zlib,
|
||||||
alejandra,
|
alejandra,
|
||||||
|
pandoc,
|
||||||
}: let
|
}: let
|
||||||
# See package.nix. Keep in sync.
|
# See package.nix. Keep in sync.
|
||||||
rpathLibs =
|
rpathLibs =
|
||||||
@ -80,6 +81,7 @@ in
|
|||||||
# For builds
|
# For builds
|
||||||
llvmPackages_latest.llvm
|
llvmPackages_latest.llvm
|
||||||
ncurses
|
ncurses
|
||||||
|
pandoc
|
||||||
pkg-config
|
pkg-config
|
||||||
scdoc
|
scdoc
|
||||||
zig
|
zig
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
ncurses,
|
ncurses,
|
||||||
pkg-config,
|
pkg-config,
|
||||||
zig_0_12,
|
zig_0_12,
|
||||||
|
pandoc,
|
||||||
revision ? "dirty",
|
revision ? "dirty",
|
||||||
}: let
|
}: let
|
||||||
# The Zig hook has no way to select the release type without actual
|
# The Zig hook has no way to select the release type without actual
|
||||||
@ -90,6 +91,7 @@ in
|
|||||||
nativeBuildInputs = [
|
nativeBuildInputs = [
|
||||||
git
|
git
|
||||||
ncurses
|
ncurses
|
||||||
|
pandoc
|
||||||
pkg-config
|
pkg-config
|
||||||
zig012Hook
|
zig012Hook
|
||||||
wrapGAppsHook4
|
wrapGAppsHook4
|
||||||
|
@ -21,6 +21,7 @@ pub const BuildConfig = struct {
|
|||||||
static: bool = false,
|
static: bool = false,
|
||||||
flatpak: bool = false,
|
flatpak: bool = false,
|
||||||
libadwaita: bool = false,
|
libadwaita: bool = false,
|
||||||
|
documentation: bool = true,
|
||||||
app_runtime: apprt.Runtime = .none,
|
app_runtime: apprt.Runtime = .none,
|
||||||
renderer: rendererpkg.Impl = .opengl,
|
renderer: rendererpkg.Impl = .opengl,
|
||||||
font_backend: font.Backend = .freetype,
|
font_backend: font.Backend = .freetype,
|
||||||
|
40
src/doc/ghostty_1_footer.md
Normal file
40
src/doc/ghostty_1_footer.md
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# FILES
|
||||||
|
|
||||||
|
_\$XDG_CONFIG_HOME/ghostty/config_
|
||||||
|
|
||||||
|
: Location of the default configuration file.
|
||||||
|
|
||||||
|
_\$LOCALAPPDATA/ghostty/config_
|
||||||
|
|
||||||
|
: **On Windows**, if _\$XDG_CONFIG_HOME_ is not set, _\$LOCALAPPDATA_ will be searched
|
||||||
|
for configuration files.
|
||||||
|
|
||||||
|
# ENVIRONMENT
|
||||||
|
|
||||||
|
**TERM**
|
||||||
|
|
||||||
|
: Defaults to `xterm-ghostty`. Can be configured with the `term` configuration option.
|
||||||
|
|
||||||
|
**GHOSTTY_RESOURCES_DIR**
|
||||||
|
|
||||||
|
: Where the Ghostty resources can be found.
|
||||||
|
|
||||||
|
**XDG_CONFIG_HOME**
|
||||||
|
|
||||||
|
: Default location for configuration files.
|
||||||
|
|
||||||
|
**LOCALAPPDATA**
|
||||||
|
|
||||||
|
: **WINDOWS ONLY:** alternate location to search for configuration files.
|
||||||
|
|
||||||
|
# BUGS
|
||||||
|
|
||||||
|
See GitHub issues: <https://github.com/mitchellh/ghostty/issues>
|
||||||
|
|
||||||
|
# AUTHOR
|
||||||
|
|
||||||
|
Mitchell Hashimoto <m@mitchellh.com>
|
||||||
|
|
||||||
|
# SEE ALSO
|
||||||
|
|
||||||
|
**ghostty(5)**
|
23
src/doc/ghostty_1_header.md
Normal file
23
src/doc/ghostty_1_header.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
% GHOSTTY(1) Version @@VERSION@@ | Ghostty terminal emulator
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
|
||||||
|
**ghostty** - Ghostty terminal emulator
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
Ghostty is a cross-platform, GPU-accelerated terminal emulator that aims to push
|
||||||
|
the boundaries of what is possible with a terminal emulator by exposing modern,
|
||||||
|
opt-in features that enable CLI tool developers to build more feature rich,
|
||||||
|
interactive applications.
|
||||||
|
|
||||||
|
There are a number of excellent terminal emulator options that exist today.
|
||||||
|
The unique goal of Ghostty is to have a platform for experimenting with modern,
|
||||||
|
optional, non-standards-compliant features to enhance the capabilities of CLI
|
||||||
|
applications. We aim to be the best in this category, and competitive in the
|
||||||
|
rest.
|
||||||
|
|
||||||
|
While aiming for this ambitious goal, Ghostty is a fully standards compliant
|
||||||
|
terminal emulator that aims to remain compatible with all existing shells and
|
||||||
|
software. You can use this as a drop-in replacement for your existing terminal
|
||||||
|
emulator.
|
32
src/doc/ghostty_5_footer.md
Normal file
32
src/doc/ghostty_5_footer.md
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# FILES
|
||||||
|
|
||||||
|
_\$XDG_CONFIG_HOME/ghostty/config_
|
||||||
|
|
||||||
|
: Location of the default configuration file.
|
||||||
|
|
||||||
|
_\$LOCALAPPDATA/ghostty/config_
|
||||||
|
|
||||||
|
: **On Windows**, if _\$XDG_CONFIG_HOME_ is not set, _\$LOCALAPPDATA_ will be searched
|
||||||
|
for configuration files.
|
||||||
|
|
||||||
|
# ENVIRONMENT
|
||||||
|
|
||||||
|
**XDG_CONFIG_HOME**
|
||||||
|
|
||||||
|
: Default location for configuration files.
|
||||||
|
|
||||||
|
**LOCALAPPDATA**
|
||||||
|
|
||||||
|
: **WINDOWS ONLY:** alternate location to search for configuration files.
|
||||||
|
|
||||||
|
# BUGS
|
||||||
|
|
||||||
|
See GitHub issues: <https://github.com/mitchellh/ghostty/issues>
|
||||||
|
|
||||||
|
# AUTHOR
|
||||||
|
|
||||||
|
Mitchell Hashimoto <m@mitchellh.com>
|
||||||
|
|
||||||
|
# SEE ALSO
|
||||||
|
|
||||||
|
**ghostty(1)**
|
90
src/doc/ghostty_5_header.md
Normal file
90
src/doc/ghostty_5_header.md
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
% GHOSTTY(5) Version @@VERSION@@ | Ghostty terminal emulator configuration file
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
|
||||||
|
**ghostty** - Ghostty terminal emulator configuration file
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
To configure Ghostty, you must use a configuration file. GUI-based configuration
|
||||||
|
is on the roadmap but not yet supported. The configuration file must be placed
|
||||||
|
at `$XDG_CONFIG_HOME/ghostty/config`, which defaults to `~/.config/ghostty/config`
|
||||||
|
if the [XDG environment is not set](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html).
|
||||||
|
|
||||||
|
The file format is documented below as an example:
|
||||||
|
|
||||||
|
# The syntax is "key = value". The whitespace around the equals doesn't matter.
|
||||||
|
background = 282c34
|
||||||
|
foreground= ffffff
|
||||||
|
|
||||||
|
# Blank lines are ignored!
|
||||||
|
|
||||||
|
keybind = ctrl+z=close_surface
|
||||||
|
keybind = ctrl+d=new_split:right
|
||||||
|
|
||||||
|
# Colors can be changed by setting the 16 colors of `palette`, which each color
|
||||||
|
# being defined as regular and bold.
|
||||||
|
#
|
||||||
|
# black
|
||||||
|
palette = 0=#1d2021
|
||||||
|
palette = 8=#7c6f64
|
||||||
|
# red
|
||||||
|
palette = 1=#cc241d
|
||||||
|
palette = 9=#fb4934
|
||||||
|
# green
|
||||||
|
palette = 2=#98971a
|
||||||
|
palette = 10=#b8bb26
|
||||||
|
# yellow
|
||||||
|
palette = 3=#d79921
|
||||||
|
palette = 11=#fabd2f
|
||||||
|
# blue
|
||||||
|
palette = 4=#458588
|
||||||
|
palette = 12=#83a598
|
||||||
|
# purple
|
||||||
|
palette = 5=#b16286
|
||||||
|
palette = 13=#d3869b
|
||||||
|
# aqua
|
||||||
|
palette = 6=#689d6a
|
||||||
|
palette = 14=#8ec07c
|
||||||
|
# white
|
||||||
|
palette = 7=#a89984
|
||||||
|
palette = 15=#fbf1c7
|
||||||
|
|
||||||
|
You can view all available configuration options and their documentation by
|
||||||
|
executing the command `ghostty +show-config --default --docs`. Note that this will
|
||||||
|
output the full default configuration with docs to stdout, so you may want to
|
||||||
|
pipe that through a pager, an editor, etc.
|
||||||
|
|
||||||
|
Note: You'll see a lot of weird blank configurations like `font-family =`. This
|
||||||
|
is a valid syntax to specify the default behavior (no value). The `+show-config`
|
||||||
|
outputs it so its clear that key is defaulting and also to have something to
|
||||||
|
attach the doc comment to.
|
||||||
|
|
||||||
|
You can also see and read all available configuration options in the source
|
||||||
|
Config structure. The available keys are the keys verbatim, and their possible
|
||||||
|
values are typically documented in the comments. You also can search for
|
||||||
|
the public config files of many Ghostty users for examples and inspiration.
|
||||||
|
|
||||||
|
## Configuration Errors
|
||||||
|
|
||||||
|
If your configuration file has any errors, Ghostty does its best to ignore
|
||||||
|
them and move on. Configuration errors currently show up in the log. The log
|
||||||
|
is written directly to stderr, so it is up to you to figure out how to access
|
||||||
|
that for your system (for now). On macOS, you can also use the system `log` CLI
|
||||||
|
utility. See the Mac App section for more information.
|
||||||
|
|
||||||
|
## Debugging Configuration
|
||||||
|
|
||||||
|
You can verify that configuration is being properly loaded by looking at the
|
||||||
|
debug output of Ghostty. Documentation for how to view the debug output is in
|
||||||
|
the "building Ghostty" section at the end of the README.
|
||||||
|
|
||||||
|
In the debug output, you should see in the first 20 lines or so messages about
|
||||||
|
loading (or not loading) a configuration file, as well as any errors it may have
|
||||||
|
encountered. Configuration errors are also shown in a dedicated window on both
|
||||||
|
macOS and Linux (GTK). Ghostty does not treat configuration errors as fatal and
|
||||||
|
will fall back to default values for erroneous keys.
|
||||||
|
|
||||||
|
You can also view the full configuration Ghostty is loading using `ghostty
|
||||||
|
+show-config` from the command-line. Use the `--help` flag to additional options
|
||||||
|
for that command.
|
16
src/generate_ghostty_1_markdown.zig
Normal file
16
src/generate_ghostty_1_markdown.zig
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const gen = @import("generate_markdown.zig");
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
const alloc = gpa.allocator();
|
||||||
|
|
||||||
|
const writer = std.io.getStdOut().writer();
|
||||||
|
|
||||||
|
try gen.substitute(alloc, @embedFile("doc/ghostty_1_header.md"), writer);
|
||||||
|
|
||||||
|
try gen.generate_actions(writer);
|
||||||
|
try gen.generate_config(writer, true);
|
||||||
|
|
||||||
|
try gen.substitute(alloc, @embedFile("doc/ghostty_1_footer.md"), writer);
|
||||||
|
}
|
15
src/generate_ghostty_5_markdown.zig
Normal file
15
src/generate_ghostty_5_markdown.zig
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const gen = @import("generate_markdown.zig");
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
const alloc = gpa.allocator();
|
||||||
|
|
||||||
|
const output = std.io.getStdOut().writer();
|
||||||
|
|
||||||
|
try gen.substitute(alloc, @embedFile("doc/ghostty_5_header.md"), output);
|
||||||
|
|
||||||
|
try gen.generate_config(output, false);
|
||||||
|
|
||||||
|
try gen.substitute(alloc, @embedFile("doc/ghostty_5_footer.md"), output);
|
||||||
|
}
|
86
src/generate_markdown.zig
Normal file
86
src/generate_markdown.zig
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const Config = @import("config/Config.zig");
|
||||||
|
const Action = @import("cli/action.zig").Action;
|
||||||
|
const help_strings = @import("help_strings");
|
||||||
|
const build_options = @import("build_options");
|
||||||
|
|
||||||
|
pub fn substitute(alloc: std.mem.Allocator, input: []const u8, writer: anytype) !void {
|
||||||
|
const version_string = try std.fmt.allocPrint(alloc, "{}", .{build_options.version});
|
||||||
|
|
||||||
|
const output = try alloc.alloc(u8, std.mem.replacementSize(u8, input, "@@VERSION@@", version_string));
|
||||||
|
defer alloc.free(output);
|
||||||
|
_ = std.mem.replace(u8, input, "@@VERSION@@", version_string, output);
|
||||||
|
try writer.writeAll(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_config(writer: anytype, cli: bool) !void {
|
||||||
|
try writer.writeAll(
|
||||||
|
\\
|
||||||
|
\\# CONFIGURATION OPTIONS
|
||||||
|
\\
|
||||||
|
\\
|
||||||
|
);
|
||||||
|
|
||||||
|
const info = @typeInfo(Config);
|
||||||
|
std.debug.assert(info == .Struct);
|
||||||
|
|
||||||
|
inline for (info.Struct.fields) |field| {
|
||||||
|
if (field.name[0] == '_') continue;
|
||||||
|
|
||||||
|
try writer.writeAll("`");
|
||||||
|
if (cli) try writer.writeAll("--");
|
||||||
|
try writer.writeAll(field.name);
|
||||||
|
try writer.writeAll("`\n\n");
|
||||||
|
if (@hasDecl(help_strings.Config, field.name)) {
|
||||||
|
var iter = std.mem.splitScalar(u8, @field(help_strings.Config, field.name), '\n');
|
||||||
|
var first = true;
|
||||||
|
while (iter.next()) |s| {
|
||||||
|
try writer.writeAll(if (first) ": " else " ");
|
||||||
|
try writer.writeAll(s);
|
||||||
|
try writer.writeAll("\n");
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
try writer.writeAll("\n\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_actions(writer: anytype) !void {
|
||||||
|
try writer.writeAll(
|
||||||
|
\\
|
||||||
|
\\# COMMAND LINE ACTIONS
|
||||||
|
\\
|
||||||
|
\\
|
||||||
|
);
|
||||||
|
|
||||||
|
const info = @typeInfo(Action);
|
||||||
|
std.debug.assert(info == .Enum);
|
||||||
|
|
||||||
|
inline for (info.Enum.fields) |field| {
|
||||||
|
if (field.name[0] == '_') continue;
|
||||||
|
|
||||||
|
const action = std.meta.stringToEnum(Action, field.name).?;
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
.help => try writer.writeAll("`--help`\n\n"),
|
||||||
|
.version => try writer.writeAll("`--version`\n\n"),
|
||||||
|
else => {
|
||||||
|
try writer.writeAll("`+");
|
||||||
|
try writer.writeAll(field.name);
|
||||||
|
try writer.writeAll("`\n\n");
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if (@hasDecl(help_strings.Action, field.name)) {
|
||||||
|
var iter = std.mem.splitScalar(u8, @field(help_strings.Action, field.name), '\n');
|
||||||
|
var first = true;
|
||||||
|
while (iter.next()) |s| {
|
||||||
|
try writer.writeAll(if (first) ": " else " ");
|
||||||
|
try writer.writeAll(s);
|
||||||
|
try writer.writeAll("\n");
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
try writer.writeAll("\n\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user