Skip to content

refactor(codegen): extract preview.zig from main_zig.zig (part of #183) - #201

Merged
apotema merged 1 commit into
feat/183-split-main-zigfrom
refactor/183-extract-preview
May 25, 2026
Merged

apotema merged 1 commit into
feat/183-split-main-zigfrom
refactor/183-extract-preview

Conversation

@apotema

@apotema apotema commented May 25, 2026

Copy link
Copy Markdown
Contributor

Summary

The biggest single extraction from src/main_zig.zig so far: the
~1200-line preview-mode codegen block. Moved to src/codegen/preview.zig
(1202 lines) and re-exported as pub consts from main_zig.zig so
every generateMainZigFromTemplate call site (the std.mem.concat
chains, the PREVIEW_INPUT_DISPATCH ternary, etc.) stays byte-identical.

main_zig.zig drops 3817 -> 2660 lines.

What moved

25 module-scope string-literal constants for the preview-mode runtime —
raylib desktop + sokol GL + sokol D3D11 + sokol Metal backend variants.
Names (all PREVIEW_* plus the WASM panic workaround living in the
same section per its docstring's note about PREVIEW_INIT_CALLBACK):

WASM_PANIC_WORKAROUND, PREVIEW_HELPERS, PREVIEW_READBACK_HELPERS,
PREVIEW_LOOP_SETUP, PREVIEW_READBACK_SETUP, PREVIEW_HEARTBEAT_LOOP,
PREVIEW_INPUT_DISPATCH, PREVIEW_INPUT_DISPATCH_STUB,
PREVIEW_READBACK_LOOP, PREVIEW_INIT_CALLBACK,
PREVIEW_CLEANUP_CALLBACK, PREVIEW_HEARTBEAT_CALLBACK,
PREVIEW_READBACK_HELPERS_SOKOL, PREVIEW_READBACK_INIT_SOKOL,
PREVIEW_READBACK_FRAME_SOKOL, PREVIEW_READBACK_CLEANUP_SOKOL,
PREVIEW_READBACK_HELPERS_SOKOL_D3D11,
PREVIEW_READBACK_HELPERS_METAL_SOKOL,
PREVIEW_READBACK_INIT_SOKOL_D3D11, PREVIEW_READBACK_INIT_METAL_SOKOL,
PREVIEW_READBACK_FRAME_SOKOL_D3D11, PREVIEW_PRE_RENDER_METAL_SOKOL,
PREVIEW_READBACK_FRAME_METAL_SOKOL,
PREVIEW_READBACK_CLEANUP_SOKOL_D3D11,
PREVIEW_READBACK_CLEANUP_METAL_SOKOL.

No wiring helper functions lived in the section the plan pointed at on
the current branch — only the constants. The 3 backend-variant
orchestration (loop / callback / readback) still lives in the
generateMainZigFromTemplate body and will move with the
lifecycle/*.zig and main_template.zig cuts later in the plan.

Risk mitigation

The plan flagged this cut as medium risk because of the 1200 lines
of inline string literals — a single whitespace edit would surface as a
generated-output diff. Verified:

  • String-literal bodies moved verbatim: diff of the moved 1194-line
    block against the pre-extraction block shows zero changes (the only
    textual edit is the pub prefix added to the 25 const lines so
    they can be re-exported).
  • Bit-identical generated output: scripts/gen_all_examples.sh
    green on all 8 working bundled examples (sokol_imgui requires the
    labelle-imgui sibling repo and soft-fails per the script's contract).
    110 generated files byte-identical; the only diffs are the 9
    documented flow_catalog.json generated_at timestamp drifts.
  • All 9 main.zig files byte-identical before vs after.
  • zig build test: 506/510 passed, 4 skipped — matches base.

No string-literal edits. No logic changes. Pure move + re-export shim.

Base-branch dependency

Stacks on top of refactor/183-extract-next (PR #195). Once that
lands, this PR's base should retarget to main.

Test plan

  • zig build clean
  • zig build test (506/510 passed, 4 skipped)
  • scripts/gen_all_examples.sh produces bit-identical generated
    output (excluding the documented flow_catalog.json timestamp)
  • String-literal bodies diff-equal to pre-extraction block

The biggest single extraction from main_zig.zig: ~1200 lines of
preview-mode codegen — the PREVIEW_* literal constants (plus the
WASM_PANIC_WORKAROUND companion that lives in the same section) for
the raylib desktop / sokol GL / sokol D3D11 / sokol Metal backend
variants. Moved to src/codegen/preview.zig (1202 lines).

main_zig.zig drops from 3817 -> 2660 lines. The 25 string-literal
constants now live in one module with `pub const` declarations and
are re-exported from main_zig.zig so every existing reference in
generateMainZigFromTemplate (the `std.mem.concat` chains, the
`PREVIEW_INPUT_DISPATCH` ternary, etc.) stays byte-identical.

The plan flagged this cut as medium risk because of the 1200 lines
of inline string literals. Verified:

  - String-literal bodies moved verbatim (diff of the moved block
    against the pre-extraction block shows only the `pub ` prefix
    on the 25 `const` lines).
  - Bit-identical generated output across all 8 working bundled
    examples (sokol_imgui needs the labelle-imgui sibling, soft-
    fails per scripts/gen_all_examples.sh's contract). 110 generated
    files byte-identical; the only diffs are the 9 documented
    flow_catalog.json `generated_at` timestamp drifts.
  - `zig build test` -> 506/510 passed, 4 skipped (matches base).

No string-literal edits. No logic changes. No `--force`-eligible
moves anywhere. Pure move + re-export shim.

Refs: #183
Builds on: #195

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request initiates a refactoring plan to split the large src/main_zig.zig file into smaller, more maintainable submodules. It introduces a detailed refactor plan document, a verification script to ensure bit-identical output, and extracts identifier helpers, preview-mode codegen templates, and discovery/scanning helpers into separate files under src/codegen/. The reviewer feedback focuses on improving code readability and idiomatic style by utilizing standard library functions for ASCII checks, and adhering to CLI best practices by replacing debug-specific printing with standard logging facilities.

Comment thread src/codegen/idents.zig
Comment on lines +41 to +51
pub fn isValidZigIdentifier(name: []const u8) bool {
if (name.len == 0) return false;
const first = name[0];
const first_ok = (first >= 'A' and first <= 'Z') or (first >= 'a' and first <= 'z') or first == '_';
if (!first_ok) return false;
for (name[1..]) |c| {
const ok = (c >= 'A' and c <= 'Z') or (c >= 'a' and c <= 'z') or (c >= '0' and c <= '9') or c == '_';
if (!ok) return false;
}
return true;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Use standard library functions std.ascii.isAlphabetic and std.ascii.isAlphanumeric instead of manual range checks to improve readability and idiomatic style.

pub fn isValidZigIdentifier(name: []const u8) bool {
    if (name.len == 0) return false;
    const first = name[0];
    const first_ok = std.ascii.isAlphabetic(first) or first == '_';
    if (!first_ok) return false;
    for (name[1..]) |c| {
        const ok = std.ascii.isAlphanumeric(c) or c == '_';
        if (!ok) return false;
    }
    return true;
}

Comment thread src/codegen/scan.zig
Comment on lines +754 to +759
const append = struct {
fn f(b: *[256]u8, idx: *usize, bytes: []const u8) void {
if (idx.* + bytes.len > b.len) {
std.debug.print("labelle: path too long for identifier (max {d} chars): too many escaped chars\n", .{b.len});
@panic("path exceeds identifier buffer size");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In CLI tools, avoid using debug-specific print functions like std.debug.print for user-facing errors or warnings. Instead, use standard logging facilities like std.log.err or write directly to stderr.

        fn f(b: *[256]u8, idx: *usize, bytes: []const u8) void {
            if (idx.* + bytes.len > b.len) {
                std.log.err("labelle: path too long for identifier (max {d} chars): too many escaped chars", .{b.len});
                @panic("path exceeds identifier buffer size");
            }
            @memcpy(b[idx.*..][0..bytes.len], bytes);
References
  1. In CLI tools, use standard logging facilities (e.g. std.log.warn) or write to stderr for user-facing warnings, rather than using debug-specific print functions (e.g. std.debug.print).

@apotema
apotema changed the base branch from main to feat/183-split-main-zig May 25, 2026 18:55
@apotema
apotema marked this pull request as ready for review May 25, 2026 20:25
@cursor

cursor Bot commented May 25, 2026 •

Copy link
Copy Markdown

PR Summary

Low Risk
Mechanical file move with alias re-exports; generated main.zig output is intended to stay byte-identical, with no runtime or preview-protocol logic changes.

Overview
This PR moves the large preview-mode codegen section out of main_zig.zig into a new src/codegen/preview.zig module (~1200 lines of PREVIEW_* string templates plus WASM_PANIC_WORKAROUND), as part of refactor #183.

main_zig.zig shrinks by replacing those inline constants with @import("codegen/preview.zig") and pub const re-exports so generateMainZigFromTemplate and existing std.mem.concat / writeAll call sites keep the same symbol names and emitted text. Preview orchestration in generateMainZigFromTemplate is unchanged; only where the literals live.

No edits to template bodies beyond making them pub in the new file for re-export.

Reviewed by Cursor Bugbot for commit 03d553f. Bugbot is set up for automated code reviews on this repo. Configure here.

@apotema
apotema merged commit f8e19aa into feat/183-split-main-zig May 25, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant