chore: scaffold Cargo workspace for the Ghostty rewrite

Start of the full Rust rewrite of Ghostty 1.3.1 (reference source in
../ghostty). Sets up a workspace whose crates mirror Ghostty's Zig modules
(vt, unicode, datastruct, os, input, config, terminal, font, renderer,
termio, core, apprt-gtk) so porting work can proceed in parallel per crate.

Also adds:
- docs/rewrite/{ARCHITECTURE,PLAN,STATUS,AGENT_GUIDE}.md: crate map, core
  design decisions (safe Rust, slab-based PageList, logical capacity
  accounting), phased plan, progress tracker, and porting rules.
- Workspace clippy config relaxing pedantic lints that are pure noise for
  low-level terminal code (casts, long functions).
- Shared vt types needed by several work packages up front: kitty
  keyboard flags (kitty/key.rs) and the CSI separator bitset (SepList).

Test Plan:
- cargo test -p ghostty-vt (3 tests pass)
- cargo clippy -p ghostty-vt --all-targets -- -D warnings
This commit is contained in:
ddidderr committed 2026-10-11 21:41:11 +02:00
1 parent 8dfec20bca
commit 864ef82acb
34 files changed
+759 -4

No files matched your search

+56
View File
@@ -0,0 +1,56 @@
# Guide for porting agents
You are porting part of Ghostty 1.3.1 (Zig) to Rust. Read
`docs/rewrite/ARCHITECTURE.md` first.
- Zig source (read-only reference): `/pantheon/pfs/git/rust/ghostty-rewrite/ghostty/src`
- Rust workspace: `/pantheon/pfs/git/rust/ghostty-rewrite/ghostty-rs`
## Rules
1. **Stay in your lane.** Only edit the crate/modules assigned to you. If you
need something from another crate that doesn't exist yet, write a minimal
local version, mark it `// TODO(integration): ...`, and mention it in your
report. Never rewrite other agents' modules. You MAY add dependencies to your
own crate's `Cargo.toml` (prefer well-known crates; use `cargo add -p <crate>`).
2. **Faithful port.** Preserve behaviour, edge cases, defaults and naming
(snake_case of the Zig names). Port doc comments (they are user-visible for
config/actions). Don't "improve" semantics. Idiomatic Rust structure is fine
(enums for tagged unions, traits for comptime interfaces, `Result` for error
unions) as long as behaviour matches.
3. **Port the tests.** Every Zig `test "name"` in files you port becomes a
`#[test] fn name_snake_case()` in a `#[cfg(test)] mod tests` of the
corresponding Rust module. Tests that are about Zig-only concerns
(allocator failure injection, comptime, C ABI, Metal/macOS) may be skipped —
list them in your notes file. Add extra tests when behaviour is subtle.
4. **No `unsafe`** unless your crate is explicitly allowed (renderer, termio,
apprt-gtk), and then only minimal + `// SAFETY:` comments.
5. **Quality gate before you finish** (must all pass for your crate):
- `cargo build -p <crate>`
- `cargo test -p <crate>`
- `cargo clippy -p <crate> --all-targets -- -D warnings`
- `cargo fmt -p <crate>` (rustfmt.toml in repo root; if nightly-only options
warn, ignore the warnings)
No `todo!()`/`unimplemented!()` left in code paths that are reachable,
unless documented in your notes as deferred.
6. **Do NOT run `git commit`** or any git command that changes state. The
orchestrator commits.
7. Other agents work concurrently in other crates of the same workspace. If the
*workspace* build breaks because of another crate, use `-p <crate>` and
ignore it. Cargo may block briefly on the build-directory lock; that's normal.
8. **Write notes** to `docs/rewrite/notes/<crate>-<topic>.md`: what was ported
(file → module), skipped tests with reasons, deviations, TODO(integration)
items, known gaps.
9. **Final report** (your last message): ≤ 25 lines — modules done, test counts
(ported / skipped), quality-gate status, open issues. The orchestrator reads
only this, so be precise and honest about anything incomplete.
## Conventions
- `pub mod` per Zig file; file names snake_case (`PageList.zig` → `page_list.rs`).
- Error types: `thiserror` is fine; `anyhow` only in binaries/CLI.
- Logging: the `log` crate (`log::warn!` etc.) where Zig uses `std.log`.
- Integer types: keep the Zig widths (u16 for cell counts `CellCountInt`, etc.).
- Zig `packed struct` bitfields → plain structs unless memory layout matters
(documented in ARCHITECTURE for `Cell`/`Row`).
- Prefer `std` collections; `hashbrown`/`smallvec`/`bitflags` OK.
+76
View File
@@ -0,0 +1,76 @@
# Ghostty-rs Architecture
Rust rewrite of Ghostty 1.3.1. Reference source: `../ghostty/` (Zig), referred
to below as `ZIG/` (= `/pantheon/pfs/git/rust/ghostty-rewrite/ghostty`).
Target platform: **Linux (GTK4 + libadwaita, OpenGL)**. macOS/Metal/CoreText,
WASM, Windows and the embedded C API (`libghostty`) are out of scope.
## Crate map (workspace `crates/`)
| Crate | Ports (ZIG/src/...) | Depends on |
|---|---|---|
| `ghostty-vt` | terminal/Parser, parse_table, UTF8Decoder, osc.zig + osc/*, csi, dcs, apc, sgr, color, x11_color, charsets, modes, ansi, device_status, mouse_shape, kitty/key, kitty/color, kitty/graphics_command, stream.zig | – |
| `ghostty-unicode` | unicode/* (width, grapheme break, props) | – |
| `ghostty-datastruct` | datastruct/* (split_tree, lru, circ_buf, blocking_queue, cache_table, ...) | – |
| `ghostty-os` | os/* (xdg, homedir, shell, locale, passwd, env, open, desktop, resourcesdir, hostname, TempDir, uri, ...) | – |
| `ghostty-input` | input/* (key, key_mods, keycodes, function_keys, key_encode, Binding, command, mouse, paste, kitty, Link) | vt |
| `ghostty-config` | config/*, cli/args.zig, cli/CommaSplitter.zig, cli/diagnostics.zig | input, os, vt |
| `ghostty-terminal` | terminal/ page, PageList, Screen, ScreenSet, Terminal, style, hyperlink, Tabstops, size, point, Selection, formatter, render, highlight, search/*, kitty/graphics_{image,storage,exec,unicode,render}, tmux/*, stream_readonly, StringMap, ref_counted_set, bitmap_allocator semantics | vt, unicode, datastruct |
| `ghostty-font` | font/* (Metrics, sprite/*, Atlas, discovery via fontconfig, face via freetype, shaper via harfbuzz, Collection, CodepointResolver, SharedGrid(Set), opentype/*, nerd_font_attributes) | config, unicode, terminal |
| `ghostty-renderer` | renderer/ generic, OpenGL (+ opengl/*), cell, cursor, image, link, size, State, Thread, Overlay, shadertoy, row, shaders/glsl | config, font, terminal |
| `ghostty-termio` | termio/*, pty.zig, Command.zig, shell-integration resources | config, terminal, os |
| `ghostty-core` | App.zig, Surface.zig, surface_mouse.zig, apprt/action.zig, apprt/structs.zig, apprt/surface.zig, global.zig, cli/* actions (+list-fonts, +show-config, ...) | everything below |
| `ghostty-apprt-gtk` | apprt/gtk/* | core, config, input |
| root `ghostty-rs` (bin `ghostty`) | main_ghostty.zig, build_config.zig | core, config, apprt-gtk |
## Key design decisions
1. **Safe Rust by default.** Workspace lint `unsafe_code = "forbid"`. Crates
that must do FFI/syscalls (renderer GL loader, termio pty/fork, apprt-gtk)
may override with `unsafe_code = "deny"` and `#[allow(unsafe_code)]` on
minimal, `// SAFETY:`-commented items. Prefer safe wrapper crates (rustix,
glow, gtk4-rs) over raw FFI.
2. **Pages without raw memory.** Ghostty's `page.zig` packs a page into one
mmap'd buffer with offsets. In Rust a `Page` owns `Vec<Row>` + `Vec<Cell>`
(row-major, `cols` per row) plus per-page `RefCountedSet<Style>`, grapheme
map `HashMap<cell_index, Vec<u32>>`, hyperlink set/map, string storage.
**Capacity accounting is preserved logically** (styles, grapheme bytes,
hyperlink bytes, string bytes, rows/cols), including Ghostty's byte-size
layout formula (`Page::layout(cap).total_size`), because PageList pruning
(`scrollback-limit` is in bytes), page splitting and capacity-adjust
behaviour — and the tests for them — depend on it.
3. **PageList as an index-based linked list.** Nodes live in a slab
(`Vec<Option<Node>>` + free list); `NodeId` is an index (+ generation for
debug checks). `Pin { node: NodeId, y, x, garbage }` is `Copy`. Tracked pins
live in a PageList-owned slab; holders keep a `TrackedPin` handle
(index) and read/write through the PageList. All operations that move
rows update tracked pins exactly like the Zig code.
4. **Cell is a compact `Copy` value** (target 8 bytes, bit-packed u64 with
accessor methods mirroring Zig field names), so memory accounting and
copying behaviour stay close to Ghostty.
5. **Parser → Stream → Handler.** `ghostty-vt::Parser` yields actions;
`ghostty-vt::stream::Stream<H: Handler>` decodes CSI/ESC/OSC/DCS/APC into
high-level actions and calls `Handler`. Terminal state changes live in
`ghostty-terminal` (`stream_readonly` handler applies to `Terminal`), and
`ghostty-termio::StreamHandler` handles side-effecting actions.
6. **Rendering:** OpenGL 4.3 via `glow`, inside a `gtk::GLArea`. Shaders are
copied verbatim from `ZIG/src/renderer/shaders/glsl`. Renderer logic
follows `renderer/generic.zig` (cell contents rebuilt from
`terminal::render::RenderState`).
7. **Fonts:** fontconfig (discovery), freetype (rasterization), harfbuzz
(shaping). Sprite font (box drawing etc.) is pure Rust, ported 1:1.
8. **Threads:** like Ghostty — per surface: IO thread (termio), renderer
thread, plus a pty read thread. Mailboxes are `crossbeam-channel` /
`std::sync::mpsc`; the GTK main thread is woken via `glib` channels.
The `Terminal` is shared behind `Arc<Mutex<..>>` (Ghostty's renderer mutex).
9. **Config:** same file format, same keys, same defaults, same CLI flags,
same docs text (used by `+show-config --docs` and `--help`).
10. **Tests:** every Zig `test "..."` block in a ported file is ported to a
Rust `#[test]` with the same name (snake_cased) in the same module.
`scripts/test-progress.sh` tracks counts.
## Out of scope / deferred
- macOS (Swift app, Metal, CoreText), Windows, WASM, libghostty C API.
- The ImGui inspector is deferred until everything else works (Phase 7).
+43
View File
@@ -0,0 +1,43 @@
# Rewrite plan
Orchestrator: Opus (plans, designs core abstractions, reviews, integrates,
commits). Bulk porting: Sonnet subagents. Hard algorithmic cores: Codex
(gpt-6-astra) or Opus. Progress lives in `STATUS.md`.
## Phases
### Phase 0 — scaffolding (done)
Workspace, crate skeletons, docs, lints.
### Phase 1 — leaf crates (parallel)
- W1 `ghostty-vt` part A: Parser, parse_table, UTF8Decoder, osc + osc/parsers/*, osc/encoding, color, x11_color, kitty/color.
- W2 `ghostty-vt` part B: csi (incl. `SepList`), sgr, modes, charsets, ansi, device_status, mouse_shape, kitty/key, kitty/graphics_command, apc, dcs.
- W3 `ghostty-unicode` + `ghostty-datastruct`.
- W4 `ghostty-os`.
- W5 `ghostty-input`.
- W6 `ghostty-config` (+ cli/args, CommaSplitter, diagnostics).
- W7 `ghostty-font`: Metrics + sprite/*.
### Phase 2 — terminal core
- `ghostty-vt::stream` (stream.zig) once W1/W2 land.
- `ghostty-terminal`: style, hyperlink, size, point, ref_counted_set, page, PageList (Codex/Opus: hardest part), then Screen, Tabstops, Terminal, ScreenSet, Selection, stream_readonly, formatter, render, highlight, search/*, kitty graphics storage/image/exec/unicode, tmux.
### Phase 3 — font engine
discovery (fontconfig), freetype face, harfbuzz shaper (+run, feature, Cache), opentype tables, Collection, CodepointResolver, CodepointMap, DeferredFace, SharedGrid, SharedGridSet, Atlas, embedded fonts, nerd_font_attributes.
### Phase 4 — termio
pty, Command (fork/exec), Exec backend, Termio, Thread, mailbox, stream_handler, shell_integration (+ copy `ZIG/src/shell-integration`).
### Phase 5 — renderer
OpenGL backend (glow), generic renderer logic, cells, cursor, image, link, size, State, Thread, Overlay, shadertoy custom shaders.
### Phase 6 — core + GTK app
ghostty-core: App, Surface, surface_mouse, apprt action/structs, CLI actions. ghostty-apprt-gtk: application, windows, tabs, splits, surface widget (GLArea, input, IME, clipboard), config reload, dialogs, notifications, quick terminal, IPC (+new-window), CSS/UI.
### Phase 7 — integration and polish
End-to-end run, vttest-ish smoke tests, perf, inspector (maybe), docs, CHANGELOG.
## Review protocol
After each work package: orchestrator reads the agent report + notes, spot-checks
code (esp. tricky logic), runs quality gate on the workspace, commits. Larger
cores get an `/astrareview` or Codex review pass.
+17
View File
@@ -0,0 +1,17 @@
# Status
Legend: ⬜ todo · 🟨 in progress · ✅ done (gate passed, committed) · ⚠️ done with gaps
| WP | Scope | Owner | State | Notes |
|---|---|---|---|---|
| P0 | scaffolding | opus | ✅ | |
| W1 | vt: parser/osc/color | sonnet | ⬜ | |
| W2 | vt: csi/sgr/modes/kitty/apc/dcs | sonnet | ⬜ | |
| W3 | unicode + datastruct | sonnet | ⬜ | |
| W4 | os | sonnet | ⬜ | |
| W5 | input | sonnet | ⬜ | |
| W6 | config | sonnet | ⬜ | |
| W7 | font: metrics + sprite | sonnet | ⬜ | |
## Log
- 2026-10-11: Phase 0 complete; Phase 1 launched.