feat(dist): website data, library installs, XCFramework, dist tarball
Closes the remaining artifact rows of the final audit (docs/rewrite/notes/final-Z4.md): - webgen: config.mdx, actions.mdx, commands.mdx (byte-identical to Zig's webgen output once its missing stdout flush is added) - `just install-lib`: libghostty (+ ghostty.h, modulemap) and libghostty-vt with versioned symlinks, headers and libghostty-vt.pc - scripts/xcframework.sh: per-arch builds, lipo and xcodebuild -create-xcframework for GhosttyKit (dry-run verified only) - `just install` builds --profile production so the installed app id is com.mitchellh.ghostty; other profiles install the -debug id/name - `just update-translations`: xgettext/msgmerge pipeline; extracted msgids equal upstream's .pot (found and fixed an untranslated string in the child-exited banner) - `just dist` / `distcheck`: versioned source tarball with VERSION and a prebuilt GTK resource bundle - Windows: icon, version resource and DPI/common-controls manifest embedded via embed-resource, GUI subsystem Test Plan: - just install-lib /tmp/p release all && pkg-config --cflags \ --libs /tmp/p/share/pkgconfig/libghostty-vt.pc - just distcheck
This commit is contained in:
1 parent
8af6ab68d3
commit
3f3f6680a5
12 files changed
+795
-23
No files matched your search
@@ -15,6 +15,7 @@ pub mod fish;
|
||||
pub mod mdgen;
|
||||
pub mod sublime;
|
||||
pub mod vim;
|
||||
pub mod webdata;
|
||||
pub mod zsh;
|
||||
|
||||
#[allow(clippy::needless_raw_string_hashes)]
|
||||
@@ -43,6 +44,14 @@ pub enum Mode {
|
||||
Man1,
|
||||
/// `ghostty(5)` man page markdown (Zig: `mdgen_ghostty_5`).
|
||||
Man5,
|
||||
/// Website `config.mdx` (Zig: `webgen_config`).
|
||||
WebdataConfig,
|
||||
/// Website `actions.mdx` (keybind actions; Zig: `webgen_actions`).
|
||||
WebdataActions,
|
||||
/// Website `commands.mdx` (CLI actions; Zig: `webgen_commands`).
|
||||
WebdataCommands,
|
||||
/// The Ghostty version string (embedded into source tarballs as `VERSION`).
|
||||
Version,
|
||||
}
|
||||
|
||||
impl Mode {
|
||||
@@ -58,6 +67,10 @@ impl Mode {
|
||||
("terminfo", Mode::Terminfo),
|
||||
("mdgen-ghostty-1", Mode::Man1),
|
||||
("mdgen-ghostty-5", Mode::Man5),
|
||||
("webgen-config", Mode::WebdataConfig),
|
||||
("webgen-actions", Mode::WebdataActions),
|
||||
("webgen-commands", Mode::WebdataCommands),
|
||||
("version", Mode::Version),
|
||||
];
|
||||
|
||||
/// Parse `+name` (Zig action syntax) or a bare `name`.
|
||||
@@ -82,6 +95,10 @@ impl Mode {
|
||||
Mode::Terminfo => ghostty_termio::terminfo::ghostty.encode_to_string(),
|
||||
Mode::Man1 => mdgen::ghostty_1(),
|
||||
Mode::Man5 => mdgen::ghostty_5(),
|
||||
Mode::WebdataConfig => webdata::config_mdx(),
|
||||
Mode::WebdataActions => webdata::actions_mdx(),
|
||||
Mode::WebdataCommands => webdata::commands_mdx(),
|
||||
Mode::Version => ghostty_cli::build_info::get().version_string.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -174,6 +191,27 @@ mod tests {
|
||||
assert!(five.contains("**`new_split`**"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn webdata_has_frontmatter_and_sections() {
|
||||
let config = webdata::config_mdx();
|
||||
assert!(config.starts_with(
|
||||
"---\ntitle: Reference\ndescription: Reference of all Ghostty configuration options.\n"
|
||||
));
|
||||
assert!(config.contains("organize these options.\n\n## `language`\n\n"));
|
||||
assert!(config.contains("## `font-family`\n"));
|
||||
assert!(!config.contains("## `_"));
|
||||
// Callouts and indented code blocks are rewritten for the website.
|
||||
assert!(config.contains("<Note>\n") || config.contains("<Warning>\n"));
|
||||
assert!(config.contains("```\n"));
|
||||
let actions = webdata::actions_mdx();
|
||||
assert!(actions.starts_with("---\ntitle: Keybinding Action Reference\n"));
|
||||
assert!(actions.contains("\n## `ignore`\nIgnore this key combination.\n"));
|
||||
let commands = webdata::commands_mdx();
|
||||
assert!(commands.contains("## version\n"));
|
||||
assert!(commands.contains("\n```\nghostty --version\n```\n\n"));
|
||||
assert!(commands.contains("\n```\nghostty +list-fonts\n```\n\n"));
|
||||
}
|
||||
|
||||
/// Every supported locale has a `.po` catalog and vice versa
|
||||
/// (Zig: `GhosttyI18n.zig` iterates `os/i18n_locales.zig`).
|
||||
#[test]
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
//! Website data generators (port of `build/GhosttyWebdata.zig` and
|
||||
//! `build/webgen/main_{config,actions,commands}.zig`). They produce the
|
||||
//! `config.mdx`, `actions.mdx` (keybind actions) and `commands.mdx` (CLI
|
||||
//! actions) files installed to `share/ghostty/webdata/`.
|
||||
|
||||
use ghostty_input::binding::ActionKey;
|
||||
|
||||
use crate::config_fields;
|
||||
|
||||
/// `webgen/main_config.zig`: the configuration reference.
|
||||
#[must_use]
|
||||
pub fn config_mdx() -> String {
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
enum Block {
|
||||
Text,
|
||||
Code,
|
||||
CalloutNote,
|
||||
CalloutWarning,
|
||||
}
|
||||
|
||||
fn end_block(w: &mut String, block: Option<Block>) {
|
||||
match block {
|
||||
Some(Block::Code) => w.push_str("```\n"),
|
||||
Some(Block::CalloutNote) => w.push_str("</Note>\n"),
|
||||
Some(Block::CalloutWarning) => w.push_str("</Warning>\n"),
|
||||
Some(Block::Text) | None => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn starts_with_ignore_case(s: &str, prefix: &str) -> bool {
|
||||
s.len() >= prefix.len()
|
||||
&& s.as_bytes()[..prefix.len()].eq_ignore_ascii_case(prefix.as_bytes())
|
||||
}
|
||||
|
||||
let mut w = String::from(
|
||||
"---\n\
|
||||
title: Reference\n\
|
||||
description: Reference of all Ghostty configuration options.\n\
|
||||
editOnGithubLink: https://github.com/ghostty-org/ghostty/edit/main/src/config/Config.zig\n\
|
||||
---\n\
|
||||
\n\
|
||||
This is a reference of all Ghostty configuration options. These\n\
|
||||
options are ordered roughly by how common they are to be used\n\
|
||||
and grouped with related options. I recommend utilizing your\n\
|
||||
browser's search functionality to find the option you're looking\n\
|
||||
for.\n\
|
||||
\n\
|
||||
In the future, we'll have a more user-friendly way to view and\n\
|
||||
organize these options.\n\
|
||||
\n",
|
||||
);
|
||||
|
||||
let fields = config_fields();
|
||||
for (i, field) in fields.iter().enumerate() {
|
||||
if field.help.is_empty() {
|
||||
continue;
|
||||
}
|
||||
w.push_str("## `");
|
||||
w.push_str(field.name);
|
||||
w.push_str("`\n");
|
||||
|
||||
// For all subsequent fields with no docs, they are grouped with the
|
||||
// previous field. (Zig breaks at `_` fields; those are already
|
||||
// filtered out of `config_fields`, which would otherwise leave a
|
||||
// gap, so the group may only extend to the next raw field.)
|
||||
for next in &fields[i + 1..] {
|
||||
if !next.help.is_empty() {
|
||||
break;
|
||||
}
|
||||
w.push_str("## `");
|
||||
w.push_str(next.name);
|
||||
w.push_str("`\n");
|
||||
}
|
||||
|
||||
w.push('\n');
|
||||
|
||||
let mut block: Option<Block> = None;
|
||||
for s in field.help.split('\n') {
|
||||
// Empty line resets our block.
|
||||
if s.is_empty() {
|
||||
end_block(&mut w, block);
|
||||
block = None;
|
||||
w.push('\n');
|
||||
continue;
|
||||
}
|
||||
|
||||
let first = block.is_none();
|
||||
if block.is_none() {
|
||||
if s.starts_with(" ") {
|
||||
block = Some(Block::Code);
|
||||
w.push_str("```\n");
|
||||
} else if starts_with_ignore_case(s, "note:") {
|
||||
block = Some(Block::CalloutNote);
|
||||
w.push_str("<Note>\n");
|
||||
} else if starts_with_ignore_case(s, "warning:") {
|
||||
block = Some(Block::CalloutWarning);
|
||||
w.push_str("<Warning>\n");
|
||||
} else {
|
||||
block = Some(Block::Text);
|
||||
}
|
||||
}
|
||||
|
||||
w.push_str(match block {
|
||||
Some(Block::CalloutNote) if first => &s["note:".len()..],
|
||||
Some(Block::CalloutWarning) if first => &s["warning:".len()..],
|
||||
Some(Block::Code) => s.strip_prefix(" ").unwrap_or(s),
|
||||
_ => s,
|
||||
});
|
||||
w.push('\n');
|
||||
}
|
||||
end_block(&mut w, block);
|
||||
w.push('\n');
|
||||
}
|
||||
w
|
||||
}
|
||||
|
||||
/// `webgen/main_actions.zig` (`input/helpgen_actions.zig`, markdown with
|
||||
/// docs): the keybind action reference.
|
||||
#[must_use]
|
||||
pub fn actions_mdx() -> String {
|
||||
let mut w = String::from(
|
||||
"---\n\
|
||||
title: Keybinding Action Reference\n\
|
||||
description: Reference of all Ghostty keybinding actions.\n\
|
||||
editOnGithubLink: https://github.com/ghostty-org/ghostty/edit/main/src/input/Binding.zig\n\
|
||||
---\n\
|
||||
\n\
|
||||
This is a reference of all Ghostty keybinding actions.\n\
|
||||
\n",
|
||||
);
|
||||
|
||||
// Doc comments are written below all related (consecutive undocumented)
|
||||
// actions, so they are buffered until the next documented action.
|
||||
let mut stream = String::new();
|
||||
for &key in ActionKey::ALL {
|
||||
let name = key.name();
|
||||
if name.starts_with('_') {
|
||||
continue;
|
||||
}
|
||||
let docs = key.help_string();
|
||||
let has_docs = !key.docs().is_empty();
|
||||
|
||||
if has_docs {
|
||||
w.push_str(&stream);
|
||||
w.push('\n');
|
||||
stream.clear();
|
||||
}
|
||||
|
||||
w.push_str("## `");
|
||||
w.push_str(name);
|
||||
w.push_str("`\n");
|
||||
|
||||
if has_docs {
|
||||
let mut iter = docs.split('\n').peekable();
|
||||
while let Some(s) = iter.next() {
|
||||
// If it is the last line and empty, then skip it.
|
||||
if iter.peek().is_none() && s.is_empty() {
|
||||
continue;
|
||||
}
|
||||
stream.push_str(s);
|
||||
stream.push('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
if !stream.is_empty() {
|
||||
w.push_str(&stream);
|
||||
}
|
||||
w
|
||||
}
|
||||
|
||||
/// `webgen/main_commands.zig`: the CLI action reference.
|
||||
#[must_use]
|
||||
pub fn commands_mdx() -> String {
|
||||
let mut w = String::from(
|
||||
"---\n\
|
||||
title: Reference\n\
|
||||
description: Reference of all Ghostty action subcommands.\n\
|
||||
editOnGithubLink: https://github.com/ghostty-org/ghostty/tree/main/src/cli\n\
|
||||
---\n\
|
||||
Ghostty includes a number of utility actions that can be accessed as subcommands.\n\
|
||||
Actions provide utilities to work with config, list keybinds, list fonts, demo themes,\n\
|
||||
and debug.\n",
|
||||
);
|
||||
|
||||
for &action in ghostty_cli::Action::ALL {
|
||||
let name = action.name();
|
||||
w.push_str("## ");
|
||||
w.push_str(name);
|
||||
w.push('\n');
|
||||
|
||||
let help = action.help_text();
|
||||
if help.is_empty() {
|
||||
continue;
|
||||
}
|
||||
for s in help.split('\n') {
|
||||
w.push_str(s);
|
||||
w.push('\n');
|
||||
}
|
||||
w.push_str("\n```\n");
|
||||
match action {
|
||||
ghostty_cli::Action::Help | ghostty_cli::Action::Version => {
|
||||
w.push_str("ghostty --");
|
||||
}
|
||||
_ => w.push_str("ghostty +"),
|
||||
}
|
||||
w.push_str(name);
|
||||
w.push_str("\n```\n\n");
|
||||
}
|
||||
w
|
||||
}
|
||||
Reference in new issue
Block a user