Hover rules in Rust, step B: Swift calls the Rust hover rules; the Swift reducers are deleted

LA
3d ago
+666-572

Step B of the hover FFI move (B8): the app's hover rules now run in Rust. Step A (#31927) landed the Rust rules and their C ABI, and the re-pins #32391 and #32459 put them in the app's CCmuxAppFFI. This PR switches the Swift types to call them and deletes the Swift copies of the rules. Nothing the person sees should change: the same rules, the same timing values, now in one place.

This PR changes no Rust: git diff origin/feat-cmux-next...HEAD -- cmux-tui is empty, and Package.swift's pin is feat's. So the Rust panic step does not apply.

What changes

The public Swift API stays the same, so HoverCardCoordinator, HoverCardPanel, TooltipPresenter and their tests do not change.

  • HoverCardMachine keeps the Rust machine's state as the C struct and calls cmux_hover_card_reduce once per event (no JSON, no heap for effects). HoverCardHandles maps each HoverTargetID to the 64-bit handle the rule compares, and converts events and effects.
  • HoverIntent (tooltips) calls cmux_hover_intent_call (HoverIntentHandles holds the target handles). CGRect.isAimedAt calls cmux_hover_is_aimed_at.
  • HoverCardEscapeGeometry (init, zone, room, rowBand, edgeGap) calls the C functions. keepsCard(at:) reads the Rust zone and maps it, with no rule of its own. rowGap stays a Swift constant (0 pt): the C ABI exports no row gap, and moving one design token would need a new step A.
  • HoverCardTiming and HoverIntentTiming.standard read cmux_hover_timing; the tunables' seconds go through cmux_hover_duration_from_seconds.
  • GroupHoverCardSummary.make calls the group_hover_summary JSON op once per card show (GroupHoverCardSummaryWire) and puts its typed lines into the localized strings.
  • webviews/test/hover-intent-parity.test.ts reads cmux-motion's hover/timing.rs, and two comments in webviews/src/ui/hoverIntent.ts name it. This is this PR's own diff, not the feat merge's: the pages keep a copy of the native hover timing (HOVER_INTENT), and the test that holds the two equal read HoverIntent.swift. That Swift file no longer holds the numbers (they come from Rust), so the test now reads the Rust file. The values are unchanged (450, 500, 200 and 300 ms).
  • plans/cmux-next/layer-ownership.md: the hover card and tooltip rules are Rust's; hit tests, the timer and the panel stay Swift's.
  • HoverCardSweepBench (off unless CMUX_HOVER_SWEEP_BENCH=1) measures the main-thread cost per pointer move of a sidebar sweep, for the perf A/B below.

Swift logic lines

python3 -I scripts/cmux-next/swift-logic-lines.py --repo . --rev <sha> --json:

shaswift_logic_linesmirror_linesunlisted lines
base (merge base with feat)736181335c9442136771121172468
headf7d0d978de2542056971121172468

-798 logic lines. The C glue and the JSON wire are their own declarations, classified PLATFORM in plans/cmux-next/swift-logic-classes.tsv. The bench added since is a test file (not counted).

Tests

  • Swift compile at the head: job 7f664a66c0e59bf8fe0f4bca (result here when it ends).
  • Swift suites on a Mac (proof-step.py): WorkspaceHoverCardBehaviorTests, HoverCardEscapeGeometryTests, HoverCardRowAlignmentTests, HoverCardWindowClampTests, HoverCardFlushTests, GroupHoverCardSummaryTests, TabHoverCardTimingTests, TooltipHoverIntentTests, ThemedTooltipTests and the SidebarHoverCard*Tests (job ids here after the compile passes).
  • bun test test/hover-intent-parity.test.ts at the head: 1 pass, 11 expect() calls.
  • Web lint (python3 scripts/web_lint_prepush.py --base origin/feat-cmux-next --head HEAD, the pre-push hook's and CI's bun run check for webviews) at 06911b587d06: "web-lint: web lint passed in 24.7s (webviews)".
  • The Rust rules' own tests are step A's (#31927).

Perf

Mini scheduler A/B, both arms on cmux18, --measurement, release build, HoverCardSweepBench: arm A the Swift rules (scratch ref rust-migration/hover-ab-base: the merge base 736181335c94 plus the bench), arm B this head. Specs rmhv-b8-ab-r1-a.json and -b.json; results here when they end. Main-thread time per pointer move must not rise.

Risk

risk:normal. Every hover card and tooltip runs through the new path; the Swift behavior suites above prove the same behavior.

Changelog

none

Observed / Not run

Observed: the merge with feat (no Rust and no pin diff), the line accounting, the pre-push gate, the web parity test and web lint.

Not run yet: the Swift compile, the Mac suites and the perf A/B (all queued above). No capture: no UI changes.