feat(cli): port ghostty +action CLI commands
Ports ZIG/src/cli into ghostty-cli: action detection/dispatch with per-action --help, and the actions help, version, list-actions, list-colors, list-keybinds, list-themes (plain listing plus an interactive ratatui browser mirroring Ghostty's libvaxis UI), show-config, validate-config, edit-config, crash-report (listing), ssh-cache and boo (235 embedded frames). Verified byte-for-byte against the installed Ghostty 1.3.1 for stdout, stderr and exit codes of most actions, incl. tty output in a pty at widths 20-200. Known differences: +list-colors --plain prints all 782 colors (Ghostty truncates at 617 because it never flushes), +version has placeholder build info until build_info::set is wired up. Deferred: list-fonts, show-face (need font discovery integration) and new-window (needs GTK IPC); they return NotYetAvailable for now. Test Plan: - cargo test -p ghostty-cli (86 tests) - cargo run -p ghostty-cli --example cli -- +list-keybinds --plain and diff against `ghostty +list-keybinds --plain`
This commit is contained in:
1 parent
a97ae04f1d
commit
6286e02f07
46 files changed
+7135
-6
No files matched your search
@@ -0,0 +1,87 @@
|
||||
# ghostty-cli port notes (C1)
|
||||
|
||||
Crate `crates/ghostty-cli` (deps: ghostty-config, ghostty-input, ghostty-os; plus
|
||||
ratatui/crossterm for TUIs, flate2 for boo frames, unicode-width). Run end to end with
|
||||
`cargo run -p ghostty-cli --example cli -- +list-actions --docs` (the example behaves
|
||||
like `ghostty` for actions via `ghostty_cli::run_from_args`, which also reproduces
|
||||
main_ghostty.zig's "multiple/unknown action" errors). Do not edit the root crate:
|
||||
wire `run_from_args(&std::env::args().collect::<Vec<_>>())` into its main later.
|
||||
|
||||
## API
|
||||
Every action module exposes `pub fn run(args: &[String]) -> Result<u8, Error>`; `args` is the
|
||||
full argv (argv[0] skipped, `+action` args ignored, like `args.argsIterator`). `--help`/`-h`
|
||||
yields `Error::ActionHelpRequested`, which `Action::run` turns into the help text (stdout, exit 0).
|
||||
`Action::{ALL,name,from_name,file,help_text,run}`, `action::{detect_iter,ActionEnum,SpecialCase}`.
|
||||
|
||||
## File -> module
|
||||
| Zig | Rust |
|
||||
|---|---|
|
||||
| cli.zig / action.zig | `action.rs` (generic detection + 7 tests) |
|
||||
| ghostty.zig | `ghostty.rs` (`Action`, dispatch, help, `run_from_args`; 5 tests) |
|
||||
| help.zig, version.zig | `help.rs`, `version.rs` (+ `build_info.rs`) |
|
||||
| list_actions/list_colors/list_keybinds | same names; pretty output via `tui.rs` |
|
||||
| list_themes.zig | `list_themes.rs` (listing) + `list_themes/preview.rs` (ratatui TUI) |
|
||||
| show_config/validate_config/edit_config/crash_report | same names |
|
||||
| ssh_cache.zig + ssh-cache/{Entry,DiskCache} | `ssh_cache.rs`, `ssh_cache/{entry,disk_cache}.rs` |
|
||||
| boo.zig (+ build/framegen/frames) | `boo.rs`, `boo_frames.deflate` (235 frames, raw deflate of frames joined by \x01) |
|
||||
| tui.zig | `tui.rs` (+ a small vaxis `prettyPrint` clone) |
|
||||
| list_fonts/show_face/new_window | stubs: Options + help + `Error::NotYetAvailable` (TODO(integration)) |
|
||||
|
||||
Help texts (`help/*.txt`) are the `run` doc comments of the Zig files, extracted with the
|
||||
helpgen rules (common-indent strip); `--help` output is byte-identical to Ghostty 1.3.1.
|
||||
|
||||
## Oracle verification (Ghostty 1.3.1 at /usr/bin/ghostty)
|
||||
Byte-identical (stdout, stderr, exit code): `+list-actions` (+`--docs`, `--help`), `+list-keybinds`
|
||||
(`--plain`, `--default`, `--docs`; also with a custom config containing chords, chains, tables,
|
||||
catch_all, unicode keys), `+list-themes --plain` (`--path`, `--color=dark|light`; with
|
||||
`GHOSTTY_RESOURCES_DIR=/usr/share/ghostty`), `+help`, `--help`, `-h`, `+validate-config` (ok and
|
||||
missing file), `+crash-report`, `+ssh-cache`, bad action / multiple actions / bad flag errors,
|
||||
`--help` of every action, `+show-config` (except the two known config deviations: `auto-update-channel`
|
||||
tip vs stable, `gtk-opengl-debug` debug build). **Pretty (tty) output of `+list-colors` and
|
||||
`+list-keybinds` is byte-identical in a pty** for widths 20..200 (including line wrapping and
|
||||
the first-row/teardown sequences of libvaxis); verified with a pty harness.
|
||||
|
||||
Known differences:
|
||||
* `+list-colors --plain`: Ghostty 1.3.1 never flushes its 4 KiB stdout buffer on this path, so its
|
||||
output is truncated at 617 of 782 lines. We print all 782 (first 617 identical).
|
||||
* `+list-colors` in a tty narrower than one column: Zig divides by zero; we clamp to one column.
|
||||
* `+version` differs in version/build info (placeholders in `build_info`; TODO(integration): root
|
||||
crate / apprt-gtk should call `build_info::set` with the real GTK/libadwaita versions, commit
|
||||
and channel). "Zig version" row became "Rust version", "libxev" row prints `n/a`.
|
||||
* `+edit-config` exec failure prints `error.FileNotFound`/`error.AccessDenied` for those kinds,
|
||||
otherwise the Rust io error text.
|
||||
* `+new-window`: the Zig parse hook swallows `--help`; ported as is. IPC not available.
|
||||
|
||||
## Interactive `+list-themes` (ratatui)
|
||||
Same layout as the vaxis version: 32-column list with `❯ name ❮` selection, preview (header, 16
|
||||
color swatches with decimal/hex numbers, 24-line code sample, lorem ipsum filler, diagnostics /
|
||||
load-error panels), help (60x22), search (3-high box at bottom) and save (92x17) overlays, all
|
||||
key bindings (q/Esc/^C, ?/F1/^H, /, ^X ^/, j/k/arrows, PgUp/PgDn (20), Home/End, h/x/d hex
|
||||
toggle, c / C copy name/path via OSC 52, f cycle filter, Enter save dialog, w write
|
||||
`auto/theme.ghostty`), mouse wheel / click / hover. Differences: no OSC 8 hyperlinks on names,
|
||||
double/dashed/dotted/curly underlines drawn as single, color scheme not queried live (defaults to
|
||||
light like Zig; `COLORFGBG` used as a hint), theme load errors during filtering are ignored
|
||||
(Zig aborts), `zf` fuzzy ranking replaced by a subsequence matcher (only matching, not order, is
|
||||
used). Logic (filtering, navigation, windowing, search, save) is unit tested; rendering is tested
|
||||
against a ratatui `Buffer`.
|
||||
|
||||
## Tests
|
||||
Ported every Zig test in the ported files: action.zig (7 detect tests), ghostty.zig (5 parse
|
||||
action tests), ssh-cache/Entry.zig (6), DiskCache.zig (4 operation tests + isValidHost,
|
||||
isValidUser, isValidCacheKey). Skipped: none. 86 tests total including new ones (help/version
|
||||
output, show-config, validate-config, crash-report, ssh-cache action, list-colors/keybinds/themes,
|
||||
tui row rendering vs vaxis bytes, boo frame parsing of all 235 frames, preview state machine,
|
||||
new-window arg hook).
|
||||
|
||||
## TODO(integration) / gaps
|
||||
* `list-fonts`, `show-face` (need ghostty-font), `new-window` (GTK IPC): return `Error::NotYetAvailable`.
|
||||
* `build_info::set` for real version/GTK info.
|
||||
* ghostty-vt `x11_color` lacks a key listing and loses name case: `list_colors` re-parses
|
||||
`ghostty-vt/src/res/rgb.txt` through a relative `include_str!` path.
|
||||
* ghostty-input `ActionKey::docs()` has an extra trailing blank line for 4 actions
|
||||
(adjust_selection, ...) compared to Ghostty's help strings; `list_actions::generate_plaintext`
|
||||
is a local copy of `helpgen_actions::generate` that normalises trailing newlines. Fix the data in
|
||||
ghostty-input and switch back to `helpgen_actions::generate`.
|
||||
* crash-report only lists (sentry is not ported). `ssh-cache` locking uses `File::try_lock`;
|
||||
like Zig, a lock held by another process is ignored (the boolean result is unused upstream).
|
||||
* `Config::load` and `finalize` are called twice in `validate-config` (as in Zig).
|
||||
Reference in new issue
Block a user