Generate documentation from GIR <doc> text#1
Merged
Merged
Conversation
Add doc: String val to RawGirNamespace, RawGirClass, RawGirInterface, RawGirRecord, RawGirEnumeration, RawGirBitfield, RawGirMember, RawGirCallback, RawGirAlias, RawGirMethod, RawGirParameter, RawGirReturnValue, RawGirProperty, RawGirSignal. Empty string when no <doc> child is present. Loader gains a _load_doc helper that finds the first <doc> child and returns its text content; every _load_* helper calls it and threads the result into the corresponding constructor. No semantic change to compile-mode emit — the new field is unused until the doc_translate package lands and the emitter is wired to read it.
Populate a by_c_type index in the validator alongside by_qname; expose resolve_by_c_type on GirModel. The doc translator needs this to map legacy gtk-doc "#GtkWidget"-style references to their owning GIR type so it can emit cross-page markdown links. c_type values that are empty strings (rare but possible — GIR allows omitting the C type for some entities) are skipped so they don't all collide on a single key.
Translates the raw <doc> text captured by the gir loader into a
markdown body suitable for use verbatim as a Pony docstring (which
pony-doc then passes through to MkDocs).
Translation set (v1):
- HTML entity decode (& < >) — defensive; libxml2 normally
handles these
- Modern gi-docgen [kind@NS.Name] refs (full set: class, iface,
struct, enum, flags, error, type, alias, method, ctor, vfunc,
func, signal, property, const)
- Legacy gtk-doc #CType refs resolved via GirModel.resolve_by_c_type
- %TRUE / %FALSE / %NULL -> `true` / `false` / `None`
- %CONSTANT -> `CONSTANT` (fallback; constants not modelled yet)
- @param -> `param`
- |[ <!-- language="X" --> … ]| -> ```X … ```
- ATX heading demotion (# -> ##, capped at H6) so docstring
headings don't collide with the page's own H1
Refs inside fenced ``` blocks, legacy |[…]| blocks, and inline
`…` spans are not rewritten — code stays code.
Diagnostics are typed (UnresolvedTypeRef, UnresolvedMethodRef,
UnresolvedConstantRef, InvalidMarkup) and accumulate in the
TranslatedDoc — callers aggregate them; the body is always usable
because unresolved refs fall back to inline-code rendering.
22 tests; counterfactual-verified by breaking diagnostic-emit,
heading-demotion, and code-block-isolation paths and confirming the
relevant assertions fired.
ClosurePlanner emits only what user code references; EverythingPlanner emits every type and every method in every loaded namespace. Used by the gir-docs binary, which generates documentation for the whole API surface regardless of what (if any) application code touches it. Also adds ScanResult.empty() — a constructor that yields a result with no source-derived inputs. The docs-mode EmitPlan stores this in its scan field; the emitter never reads scan today, but keeping the field populated preserves the invariant that an EmitPlan always has one.
Emitter.apply gains a new emit_docstrings: Bool = false parameter. When true, each per-kind generator (class, interface, record, enum, bitfield, callback, alias) prepends the GIR <doc> text, translated via doc_translate, as a Pony docstring inside the generated type. MethodSpec and SkippedSpec gain a doc field; MethodEmitter threads the translate context into _emit_trivial_void, _emit_trivial_return, _emit_constructor_floating, _emit_signal_connect, and _emit_skip_stub so method-level docstrings land alongside the method signature. A new DocstringWriter helper handles indent + triple-quote formatting, so each generator just appends a single helper call. Empirically verified that ''' does not appear in any of Gtk-4.0, Gio-2.0, GLib-2.0, or GObject-2.0 doc text — no need to escape v1. Default emit_docstrings=false preserves compile-mode output exactly; the gtk4 hello example still builds and runs unchanged. 4 emitter tests + counterfactual-verified the writer's closing fence assertion fires when broken.
Sibling executable to gir-compiler that runs the full pipeline with emit_docstrings=true and EverythingPlanner. Output is a self- contained Pony tree of every GIR type with translated docstrings that pony-doc can ingest into an MkDocs site. The Makefile now builds both binaries by default. Each has its own SRC_DIR (bin/ for gir-compiler, gir-docs/ for gir-docs) but shares the gir/scanner/planner/emitter/doc_translate library packages. Smoke test against the four target namespaces (Gtk-4.0, Gio-2.0, GObject-2.0, GLib-2.0) emits 1530 files / 3.7MB. Sample inspection confirms type-level and method-level docstrings are attached. Note: pony-doc against the full output currently fails because the compile-mode emitter has pre-existing bugs — variadic params, array<T> syntax, methods named 'new', etc. — that didn't show up in the small hello closure. Those are tracked separately; gir-docs as a plumbing piece is complete.
The compile-mode emitter now also attaches translated GIR <doc> text to generated bindings, on parity with gir-docs. Closure-mode users get hover-doc / pony-doc benefits on whatever subset of the API their code touches. Verified: gtk4 hello example still builds; generated build/hello/gtk/Application.pony carries the GtkApplication docstring; the binary runs end-to-end.
Each Gen* iterates node.target.constructors directly to emit constructors via MethodEmitter.classify_constructor. Adding the same constructors to plan.method_calls caused double-emission — once as 'new create(...)' from the constructor pass, once as 'fun ref <name>()' from method_calls (which collides with Pony's 'new' keyword). Drops 180 of 244 syntax errors from a full Gtk-4.0 emit.
The GIR loader maps <varargs/> to RawGirType("varargs", "").
_pony_type_for had no case for the varargs sentinel, so it fell
through to _resolve_object_type and produced PtGObject("Gtk.varargs",
"Gtkvarargs"). That made the whole method classify successfully and
emit invalid FFI like:
use @gtk_alert_dialog_new[Pointer[U8] tag](
format: Pointer[U8] tag, ...: Pointer[U8] tag)
(the literal '...' GIR parameter name reaches the Pony source).
Adding 'varargs' to the type-name match returns UnemittableVariadic;
the method becomes a SkippedSpec with a compile_error stub. Drops
the remaining FFI-parameter errors (30 -> 0) and several 'parameters'
errors from a full Gtk-4.0 emit.
The GIR loader maps <array><type name=X/></array> to RawGirType(
"array<X>", c_type). _pony_type_for had no case for this sentinel,
so it fell through to _resolve_object_type and produced
PtGObject("Gtk.array<utf8>", "Gtkarray<utf8>"). The emitted Pony
then had things like `people: Gtkarray<utf8> box` in parameters
and return positions, both unparseable.
Same posture as the varargs fix: detect the sentinel at the type-
mapping boundary and surface as UnemittableUnsupportedShape so the
method becomes a compile_error skip stub. The full v1 array shape
(probably Array[T] for safe inner types) is deferred until the FFI
marshalling is designed.
Drops 22 of the remaining 25 syntax errors in a full Gtk-4.0 emit
(13 "parameters" + 9 "< after type").
Extract the GIR-name -> Pony-identifier munge into a new primitive
PonyIdent in the gir/ package. It owns the reserved-word catalog
(match, ref, error, box, val, iso, trn, new, etc.), hyphen-to-
underscore conversion, trailing-underscore strip, and the prime-
suffix collision rule.
Three callers now route through the same function:
- emitter/TypeNaming.safe_param_name delegates (was previously the
sole owner of the munge and reserved-word list)
- emitter/MethodEmitter._classify_raw munges pony_name into
MethodSpec so the emitted Pony method has a legal identifier
- emitter/MethodEmitter._wrap_skip munges into SkippedSpec so
skip-stub methods also get the safe name (otherwise reserved
words slip past the compile_error stub path)
- doc_translate/UrlBuilder.member_link munges the URL anchor and
visible label so the doc cross-reference matches the actual
emitted method name
Without the doc-side munge, [method@Gtk.Filter.match] in GIR docs
would render as a link to #match but pony-doc would generate the
anchor as #match' (matching the method's Pony identifier), and the
link would dangle.
Adds gir/_test.pony with 8 PonyIdent example tests plus a totality
property; doc_translate gains a test that the URL anchor uses the
munged name (counterfactual-verified by bypassing the munge and
confirming the assertion fires).
Drops the last 3 syntax errors from a full Gtk-4.0 emit; all four
target packages (Gtk-4.0, Gio-2.0, GObject-2.0, GLib-2.0) now parse
cleanly via ponyc.
The prior commit used PonyIdent.safe for both parameter and method
names, which suffixes a prime (') on reserved-word collisions. That
works for parameters and let/var bindings (Pony accepts primes in
those positions) but fails on method declarations:
/tmp/gen-docs/gtk/Filter.pony: method name "match'" cannot contain prime
Pony's method-name grammar also rejects trailing underscores and
double underscores, leaving no inline-suffix scheme. Prefix it is:
PonyIdent.safe_method munges reserved words with a 'g' prefix
(echoing the GObject naming convention), keeping the method name a
legal identifier.
PonyIdent now exposes two routes:
- safe_param — for parameter / binding names (match -> match')
- safe_method — for method / function names (match -> gmatch)
A shared _normalize_and_trim helper handles the hyphen / trailing-
underscore steps both routes share.
Callers updated:
- TypeNaming.safe_param_name -> safe_param
- MethodEmitter._classify_raw -> safe_method
- MethodEmitter._wrap_skip -> safe_method
- doc_translate UrlBuilder.member_link -> safe_method
- gir/_test.pony tests rewritten for both routes
- doc_translate test for the match reservation now expects gmatch
ponyc against the full Gtk-4.0/Gio-2.0/GLib-2.0/GObject-2.0 emit
now parses with zero syntax errors. pony-doc reaches the semantic
phase and surfaces a new bug class (duplicate method names from
multiple constructors all rendering as `create`) — tracked
separately.
The Pony spelling for a GIR type now comes from the GIR-declared
c:type directly. So Gio.Application -> GApplication (not
GioApplication), GObject.Object -> GObject (not GObjectObject),
GLib.Bytes -> GBytes (not GLibBytes). For namespaces where the c:type
already matches the GIR namespace prefix (most of Gtk-4.0), this is a
no-op: Gtk.Widget stays GtkWidget either way.
Why: the C-side ecosystem already uses these names — gtk-doc,
pkg-config, header files, and downstream library tutorials all spell
the types as the c:type. Prepending the GIR namespace ('Gio' instead
of 'G') produces names that don't appear anywhere in C-side prior art
and require constant mental translation when reading GTK docs and
the Pony bindings side-by-side.
Changes:
- TypeNaming.pony_type_name now takes (c_type, qname_fallback). The
c_type takes precedence; the namespace-prepended form is the
fallback only when c:type is empty.
- TypeNaming.pony_name_for_node centralizes the per-kind c:type
extraction (class/iface/record/enum/bitfield/callback/alias).
- MethodEmitter._pony_type_for now reads c:type off the resolved
node via pony_name_for_node, so PtGObject / PtBitfield / PtEnum
carry the c:type spelling.
- _resolve_object_type became _resolve_object_qname — returns just
the qname; pony-side name now sourced from the model lookup.
- doc_translate's UrlBuilder.type_page / type_link / member_link
take c_type and produce <pkg>-<c_type>.md, matching what
pony-doc actually emits when it compiles the generated package.
- doc_translate gains a _node_c_type helper paralleling
_node_namespace / _node_local_name. The legacy #CType resolver
and the modern [class@…] / [method@…] resolvers both route
through it.
- Tests updated: doc_translate's link-format expectations now
use the c:type ('GtkWidget' instead of 'Widget' in the label,
'gtk-GtkWidget.md' instead of 'gtk-Widget.md' in the URL).
Smoke-test on Gtk-4.0/Gio-2.0/GLib-2.0/GObject-2.0 full emit: 0
syntax errors across all four packages; hello example still
builds and runs.
Three changes that together drive a full Gtk-4.0/Gio-2.0/GLib-2.0/ GObject-2.0 emit to zero parse + name errors: 1. Constructor names derived from raw GIR name - 'new' -> 'create' - 'new_with_label' -> 'with_label' (strip the 'new_' prefix) - 'new_from_file' -> 'from_file' - 'newv' -> 'newv' (no underscore, used verbatim) _emit_constructor_floating now uses spec.pony_name instead of hardcoded 'create'. Classes with multiple GIR constructors (gtk_button_new + gtk_button_new_with_label + ...) get distinct Pony constructor slots; 44 'can't reuse name create' errors gone. The public helper MethodEmitter.constructor_pony_name exposes the mapping so gen_class can compute the same name up front. 2. Class-wide parameter name disambiguation gen_class builds a Set[String val] of every Pony name that will land on the class — every constructor's slot name, every method call name, and the synthetic _h / _runtime / _wrap / _handle / _runtime_ref names. The set threads through classify_method / classify_constructor / _classify_raw. When a safe parameter name matches anything in the set, the param gets a prime suffix. Catches the 'page_num / has_audio / event' family of collisions where one method on the class is named e.g. 'page_num' and other methods take a 'page_num' parameter. 3. Skip stubs removed entirely When MethodEmitter.classify_* returns SkippedSpec, the emitter now emits nothing at all — no compile_error stub, no ifdef block. If a method isn't representable in Pony, the binding simply doesn't expose it. Users who try to call something that isn't there get ponyc's normal 'method not found', which is fine since the method genuinely doesn't exist in the Pony API surface. The _emit_skip_stub and _describe helpers are now dead and deleted. Hello example still builds + runs; all package tests still pass (gir 10, emitter 4, doc_translate 23).
Generated classes that reference types from other GIR namespaces now declare a `use "../<ns>"` directive for each such namespace, alongside the existing `use "../gobject_runtime"` and `use "lib:..."` lines. gen_class walks every MethodSpec's parameter and return types, extracts the qname of each PtGObject / PtBitfield / PtEnum, and collects the set of distinct namespaces that aren't the receiver's own. Companion change in method_emitter: when a type reference resolves to None (the type lives in a namespace we didn't load) or to a kind we don't yet emit (callbacks, aliases via the catch-all arm), the classifier now produces UnemittableUnknownType instead of falling back to PtGObject. Methods touching such types are skipped — which also means we don't emit `use "../<unloaded-ns>"` directives for packages that don't exist in the target tree. Cross-package types that *are* loaded resolve correctly: a Gtk method taking Gio.Application as a parameter now emits with the receiver class declaring `use "../gio"` and the parameter typed as `GApplication box`. Hello example still builds + runs; full Gtk/Gio/GLib/GObject emit goes from 'can't find definition of <Type>' (one per cross-package reference) down to a much narrower set of remaining issues that are unrelated to imports (duplicate type names across packages like GIOCondition in both GLib and GObject; record marshalling for PtGObject; return-type wrapping for utf8 returns; GtkRuntime cycle for the rare non-Gtk classes that we declare `_runtime: GtkRuntime tag` on).
GLib and GObject both declare a bitfield named IOCondition with the
same c:type (GIOCondition); GIR carries them in two separate
namespaces but they represent the same C type. The previous emit
produced two Pony declarations (glib/IOCondition.pony and
gobject/IOCondition.pony, both defining a 'GIOCondition' primitive),
which clashed at every consumer that imported both packages.
Three coordinated changes:
1. GirValidator's by_c_type index becomes first-wins: when a c:type
is already registered, the later declaration is left out of the
index. Whichever namespace appears first in the user's --gir
list owns the slot.
2. MethodEmitter._pony_type_for routes through by_c_type after
resolving the gir_name to a node. The PonyType variants
(PtBitfield / PtEnum / PtGObject) carry the canonical qname, so
the foreign-namespace tracker in gen_class adds a use directive
for the canonical package — not whatever package happened to
own the gir_name's owner namespace.
3. Emitter._content_for_node detects when a node's c:type maps to
a different canonical node and emits a comment-only stub file
('// Duplicate declaration — canonically declared as …')
instead of dispatching to the per-kind generator. The package
directory layout stays intact; pony-doc just sees one
definition globally.
A new NodeHelpers primitive in gir/ centralizes
namespace_of/local_name_of/c_type_of/qname_of on GirNodeRef so the
emitter doesn't repeat seven-arm matches for the same field reads.
Full-corpus emit drops 'duplicate type clash' errors from 14 to 0;
remaining errors are unrelated (#100, #101, #102). Hello example
still builds + runs; all package tests pass.
Generated classes previously declared `let _runtime: GtkRuntime tag`
on every emitted type. That works inside the gtk package (where
GtkRuntime is defined) but creates a cycle for any non-gtk package
that emits classes: gobject/Object.pony, gio/File.pony, etc. would
need `use "../gtk"` to find GtkRuntime, but gtk imports gobject/
gio/glib for its own type references — closing the loop.
Fix: declare a structural `interface tag PinnedRuntime` in
gobject_runtime/pinned_runtime.pony with the minimal set of methods
generated bindings invoke (currently just `register_close_request`).
Generated classes now hold `_runtime: PinnedRuntime tag` and use
`_wrap(h, runtime: PinnedRuntime tag)` / `_runtime_ref():
PinnedRuntime tag`. GtkRuntime's matching behavior makes a
`GtkRuntime tag` structurally assignable to a `PinnedRuntime tag`,
so the user's hello example still passes its concrete GtkRuntime to
generated constructors and signal-connect helpers.
Embedded changes:
- new embedded/gobject_runtime/pinned_runtime.pony declaring
CloseRequestHandler (moved here from gtk/runtime.pony) and the
PinnedRuntime interface.
- embedded/gtk/runtime.pony's `_register_close_request` is now
public `register_close_request` (Pony's package-private
underscore convention would otherwise bar cross-package calls
through the structural interface).
- tools/bake_embedded.sh ships the new file; the bake target
re-runs and emitter/embedded_resources.pony picks up the third
entry.
Emitter changes:
- gen_class emits `_runtime: PinnedRuntime tag` and updates the
_wrap / _runtime_ref signatures to match.
- MethodEmitter._emit_signal_connect calls
`_runtime.register_close_request` (no leading underscore).
Hello example still builds + runs unchanged — the user types the
runtime as GtkRuntime and structural matching handles the rest.
All 'can't find definition of GtkRuntime' errors are gone (4 packages
× 1 each → 0). Remaining errors in the full-corpus emit are all
#100 (return-type wrapping) and #101 (record marshalling),
previously masked by the runtime error stopping ponyc per-file.
The emitted method bodies previously just dumped the FFI call result,
declaring a typed Pony return that didn't match — `fun ref get_title():
String` with a body of `@ffi(_h.raw())` (which is Pointer[U8] tag).
ponyc rejected with 'function body isn't the result type', and a
parallel mismatch happened on the parameter side when records used
the wrong accessor (`closure._handle().raw()` on a record that only
has `.raw()`).
Coordinated changes:
1. PtGObject gains a `kind` field — PtGObjectClass, PtGObjectInterface,
or PtGObjectRecord — set in `_pony_type_for` based on the
resolved GIR node kind. The marshaller and return wrapper both
dispatch on it.
2. Underscore-prefixed helpers `_wrap`, `_handle`, `_runtime_ref`
are now public `wrap`, `handle`, `runtime_ref` so cross-package
wrapping at return sites works. The leading underscore was making
them package-private, which blocked emitting `OtherPkg.Type.wrap(...)`
from a different package.
3. Interfaces emit as class-shaped wrappers (with `_h`, `_runtime`,
`wrap`, `handle`, `runtime_ref`) instead of empty traits, so an
interface-typed return value has a wrapper shape to land in.
4. New `_wrap_return` helper in MethodEmitter wraps the FFI call
result based on return type:
utf8 -> `String.from_cstring(<ffi>).clone()`
class/iface-> `<Type>.wrap(GObjectHandle.adopt_borrowed(<ffi>), _runtime)`
record -> `<Type>.wrap(<ffi>)`
primitive -> pass through
5. The FFI declaration's return type splits from the param type
for utf8: returns are typed `Pointer[U8]` (ref, what String.from_cstring
expects) while params stay `Pointer[U8] tag` (what .cstring() produces).
6. `_marshal_arg` dispatches on PtGObjectKind: `.handle().raw()` for
class/interface, `.raw()` for record. Closes #101.
7. Constructor _runtime sourcing skips PtGObjectRecord params (they
have no runtime_ref to read).
8. Methods that can't be wrapped become UnemittableUnsupportedShape
and are skipped:
- methods returning PtBitfield/PtEnum (no from-integer wrap yet)
- constructors with no class/interface GObject parameter to
source _runtime from (would leave _runtime uninitialized)
Full Gtk-4.0/Gio-2.0/GLib-2.0/GObject-2.0 emit:
gtk 1456 errors -> 0 (the 'no Main actor' notice that ponyc emits
gio 507 errors -> 0 when run directly on a library package is
gobject 30 errors -> 0 informational only — pony-doc reaches the
glib 1 error -> 0 generator without it.)
pony-doc end-to-end on the gtk package now succeeds and produces a
3624-file MkDocs tree. Hello example still builds + runs; all package
tests pass (gir 10, emitter 4, doc_translate 23).
Brings gir-compiler's GitHub Actions setup in line with libxml2:
- pr.yml — adds super-linter and changelog-tool verification
alongside the existing build job; smoke-tests both binaries
(gir-compiler + gir-docs) after `make`.
- main.yml — also smoke-tests both binaries on push to main.
- lint-action-workflows.yml — actionlint on PRs.
- changelog-bot.yml — keeps CHANGELOG.md current per push.
- release-notes.yml + release-notes-reminder.yml — manage the
.release-notes/ folder (created with an empty next-release.md).
- prepare-for-a-release.yml — bumps CHANGELOG/VERSION/README/
corral.json on release-X.Y.Z tags, then triggers tag-release.
- release.yml — validates CHANGELOG, rebuilds, and triggers
announce on X.Y.Z tags.
- announce-a-release.yml — publishes release notes to GitHub +
Zulip on announce-X.Y.Z tags.
- generate-documentation.yml — manual-trigger workflow that
deploys pony-doc output to GitHub Pages at gir-compiler.contact.red,
with the docs-theme/ contact.red branding applied (CSS copied
from libxml2).
These run against the same ghcr.io/redvers/pony-dependency-builder
and ghcr.io/ponylang/* images libxml2 uses. The release workflows
need the same secrets the libxml2 release pipeline uses
(RELEASE_TOKEN, ZULIP_*, CONTACTRED_MAIN_API_TOKEN) to fire end-to-end.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
<doc>text is ingested, translated from gtk-doc / gi-docgen markup to Pony-docstring markdown, attached to every emitted type and method, and the resulting package tree is consumable bypony-doc.gir-docsbinary that runs the pipeline in full-namespace mode (no closure analysis) against Gtk-4.0 / Gio-2.0 / GObject-2.0 / GLib-2.0 and produces a buildable Pony tree thatponycparses + name-checks with zero errors.usedirectives, duplicate type dedup, the GtkRuntime cycle (via aPinnedRuntimestructural interface in gobject_runtime), record vs class wrapper shapes, and return-value wrapping for utf8 / object types.Commits
Foundation: capture
<doc>text on every raw GIR entity ·GirModel.resolve_by_c_type·doc_translatepackage ·EverythingPlanner·gir-docsbinary + Makefile · emitter docstring hook · compile-mode parity.Emitter correctness: skip
varargsandarray<T>methods · munge reserved-word method names via sharedPonyIdent· usec:typefor class names · disambiguate constructor names · dropcompile_errorskip stubs · sibling-packageusedirectives · dedupe types declared in multiple namespaces · break GtkRuntime cycle · wrap return values.Verification
pony-doc -o /tmp/docs-mkdocs gtkagainst the full emit succeeds and produces a 3624-file MkDocs tree. ponyc errors per package on the full Gtk-4.0/Gio-2.0/GLib-2.0/GObject-2.0 emit: 0 across all four packages.Test plan
make helloin the gtk4 consumer still produces a binary that runs (activate handler running on the pinned thread/window presented).corral run -- ponyc gir,corral run -- ponyc doc_translate,corral run -- ponyc emitter(gir 10, doc_translate 23, emitter 4).gir-docs --gir Gtk-4.0,Gio-2.0,GObject-2.0,GLib-2.0 --target <dir>emits a Pony tree that ponyc parses without errors.pony-doc -o <out> <gen-dir>/gtksucceeds and produces an MkDocs site.