Extract Ghostty config-file discovery from GhosttyTerminalView into CmuxGhosttyConfigLoader

Closed
AZ
4mo ago
+1067-458

Pulls the pure Ghostty config-file discovery + scanning domain out of GhosttyApp (inside the ~12k-line Sources/GhosttyTerminalView.swift god file) into a new leaf package CmuxGhosttyConfigLoader, as a constructor-injected value type GhosttyConfigDiscovery.

What moved

Now a real type with inverted seams instead of statics welded onto GhosttyApp:

  • CJK font fallback: cjkFontMappings, autoInjectedCJKFontMappings, shouldInjectCJKFontFallback, resolvedInjectedCJKFontName, glyph-coverage probing.
  • User font-config scanning: userFontConfigSummary, config-file include following, parsedConfigEntry / applyConfigFileDirective, UserFontConfigSummary.
  • Scan-path resolution: loadedGhosttyConfigScanPaths / loadedCJKScanPaths, cmuxAppSupportConfigURLs.
  • Legacy-config decisions: shouldLoadLegacyGhosttyConfig, shouldIncludeLegacyGhosttyConfigInScanPaths, shouldIgnoreNativeLegacyBaselineForUnparsedAppearance.
  • Conditional-theme override + managed-default-appearance decisions.

Seams (inverted, constructor-injected; defaults reproduce prior behavior)

  • GhosttyConfigFileReading (default FileManagerGhosttyConfigFileReader) inverts every config file size/contents read that used FileManager.default / String(contentsOfFile:).
  • GhosttyFontProbing (default CoreTextGhosttyFontProbe) inverts the CoreText CTFont family-name discovery and glyph-coverage probing.
  • CmuxGhosttyConfigPathResolver (from CmuxFoundation) injected for app-support URL resolution.

One major public type per file; /// DocC on every public symbol; no caseless-enum namespace, no free functions, no lint:allow.

Byte-identical

Every directive key, scan-path ordering, legacy-fallback size rule, config-file include semantics, CJK range set, and theme-override text is preserved exactly. The C-API config-load instance methods (loadDefaultConfigFilesWithLegacyFallback, loadCJKFontFallbackIfNeeded, reloadConfiguration, synchronizeThemeWithAppearance) stay in the app target and now call the new type to decide what to load while still issuing the ghostty_config_load_* calls themselves. The former GhosttyApp statics remain as thin one-line forwards, so all call sites (including cmuxTests) are unchanged.

Left in the app target on purpose: everything that mutates ghostty_config_t, GhosttyApp instance state, or reaches AppDelegate.shared / NotificationCenter — not pure, not part of this clean subset.

The seam is a synchronous value type (not an actor) because discovery runs inside the synchronous launch-path config-load sequence; an async actor would reorder the C-API calls. Principled over the actor shape the audit suggested.

Tests

17 behavior unit tests with in-memory fakes for both seams (CJK mapping, font-config scanning incl. config-file includes, legacy-config rules, scan-path composition, theme override).

Stats / wiring

  • Giant GhosttyTerminalView.swift shrinks 12093 → 11691 lines (−402); .github/swift-file-length-budget.tsv reconciled.
  • Wired into cmux.xcodeproj (6 pbxproj entries) + app-target dependency.

NOTE: full app build is validated by GitHub CI; local app xcodebuild was skipped intentionally (parallel refactor agents). The new package builds and tests pass locally, and scripts/lint-ios-package-conventions.sh is clean with zero new lint:allow.


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Summary by cubic

Extracted Ghostty config discovery/scanning into a new leaf package CmuxGhosttyConfigLoader with GhosttyConfigDiscovery, reducing GhosttyTerminalView.swift by 402 lines and keeping behavior identical. Improves testability and isolates pure logic from app state.

  • Refactors
    • Moved: CJK font fallback, user font-config scanning (with includes), scan-path resolution, legacy-config decisions, and theme override/managed default appearance into GhosttyConfigDiscovery.
    • Added seams: GhosttyConfigFileReading (default FileManagerGhosttyConfigFileReader), GhosttyFontProbing (default CoreTextGhosttyFontProbe); injects CmuxGhosttyConfigPathResolver.
    • App’s C-API config-loaders remain and now delegate to the new type while performing ghostty_config_* calls.
    • Preserves behavior: same scan-path ordering, legacy size rule, include semantics, CJK ranges, and theme override text.
    • Added 17 unit tests with in-memory fakes and small C stubs; wired CmuxGhosttyConfigLoader into cmux.xcodeproj.