Files
ghostty-rs/crates/ghostty-input/src/helpgen_actions.rs
T
ddidderr f4c020101c fix: close remaining gaps found by the completeness audit
Addresses the partial ports listed in
docs/rewrite/notes/review-completeness-codex.md (details in
docs/rewrite/notes/gaps-X2.md):
- Flatpak: the exit watcher no longer consumes the host command; a
  shared FlatpakSignalHandle lets closing a surface still SIGHUP the
  host process group.
- Child environment is byte-preserving end to end (ghostty_os::EnvMap
  of OsStrings) and Snap paths are filtered like filterSnapPaths.
- argv handling no longer panics on non-UTF-8 arguments.
- URI percent-decoding keeps raw bytes; OSC 7 pwd is passed through
  without lossy conversion.
- Keybinding comparison/hashing uses full Unicode case folding from a
  table generated from CaseFolding.txt (Unicode 17.0.0).
- Link regexes set Oniguruma's whole-search retry limit through the new
  minimal ghostty-onig crate (one SAFETY-commented FFI call).
- OSC 9 NUL-capacity check ordering matches Zig.
- Wayland registry binds/clears globals advertised after startup.
- Custom shaders may pass sampler2D to helper functions (naga adapter).
- +list-themes preview renders OSC 8 links, styled underlines and
  follows color-scheme reports.

Also includes the inspector's integration edits in shared files
(renderer state hook, termio VT recording, core/apprt-gtk wiring), X1's
config/input/cli additions for completion generation, and a fix for a
race in the termio backpressure test (it wrote before `stty raw`
applied, so the cooked line discipline dropped bytes past MAX_CANON).

Test Plan:
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
2026-10-12 04:39:24 +02:00

149 lines
4.3 KiB
Rust

//! This module is a help generator for keybind actions documentation.
//! It can generate documentation in different formats (plaintext for CLI,
//! markdown for website) while maintaining consistent content.
//! (ZIG/src/input/helpgen_actions.zig)
//!
//! The docs themselves live in [`ActionKey::docs`] (the Zig version reads
//! them from the build-time generated `help_strings`).
use crate::binding::ActionKey;
/// Format options for generating keybind actions documentation
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Format {
/// Plain text output with indentation
Plaintext,
/// Markdown formatted output
Markdown,
}
impl Format {
fn format_field_name(self, w: &mut String, field_name: &str) {
match self {
Format::Plaintext => {
w.push_str(field_name);
w.push_str(":\n");
}
Format::Markdown => {
w.push_str("## `");
w.push_str(field_name);
w.push_str("`\n");
}
}
}
fn format_doc_line(self, w: &mut String, line: &str) {
match self {
Format::Plaintext => {
w.push_str(" ");
w.push_str(line);
w.push('\n');
}
Format::Markdown => {
w.push_str(line);
w.push('\n');
}
}
}
fn header(self) -> Option<&'static str> {
match self {
Format::Plaintext => None,
Format::Markdown => Some(
"---\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\
\n",
),
}
}
}
/// Generate keybind actions documentation with the specified format
pub fn generate(writer: &mut String, format: Format, show_docs: bool) {
if let Some(header) = format.header() {
writer.push_str(header);
}
let mut stream = String::new();
for &key in ActionKey::ALL {
let name = key.name();
if name.starts_with('_') {
continue;
}
// Write previously stored doc comment below all related actions
// (every action has a doc comment).
if show_docs {
writer.push_str(&stream);
writer.push('\n');
stream.clear();
}
if show_docs {
format.format_field_name(writer, name);
} else {
writer.push_str(name);
writer.push('\n');
}
if show_docs {
let mut iter = key.help_string().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;
}
format.format_doc_line(&mut stream, s);
}
}
}
// Write any remaining buffered documentation
if !stream.is_empty() {
writer.push_str(&stream);
}
}
/// Convenience: generate into a fresh string.
pub fn generate_string(format: Format, show_docs: bool) -> String {
let mut s = String::new();
generate(&mut s, format, show_docs);
s
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn plaintext_with_docs_lists_all_actions() {
let s = generate_string(Format::Plaintext, true);
assert!(s.starts_with("\nignore:\n"));
assert!(s.contains("ignore:\n Ignore this key combination.\n"));
for &k in ActionKey::ALL {
assert!(s.contains(&format!("{}:\n", k.name())));
}
}
#[test]
fn names_only() {
let s = generate_string(Format::Plaintext, false);
assert_eq!(s.lines().count(), ActionKey::ALL.len());
assert_eq!(s.lines().next(), Some("ignore"));
}
#[test]
fn markdown_header_and_field() {
let s = generate_string(Format::Markdown, true);
assert!(s.starts_with("---\ntitle: Keybinding Action Reference\n"));
assert!(s.contains("## `unbind`\n"));
}
}