CodeEditApp / CodeEditApp/CodeEditLanguages

Syntax highlighting silently fails (and `Bundle.module` crashes) in SwiftPM CLI-built app bundles

Đang mở
#95 0 bình luận 0 reaction 0 người được giao Xem trên GitHub
Ngôn ngữ chính
Swift
Star
133
Fork
55
Chỉ số merge pull request
Không có pull request nào được merge trong 30 ngày

Mô tả

## Summary

When `CodeEditLanguages` is consumed by an app that is **built and packaged
with `swift build` (plus a hand-rolled bundle-assembly step) rather than
Xcode**, two resource-resolution problems surface:

1. **Distributed app crashes** on the first highlight lookup, because the
SwiftPM-generated `Bundle.module` accessor cannot locate the resource
bundle inside a `.app` and calls `fatalError`.
2. Even with the bundle located, **every tree-sitter query file fails to
load** because `CodeLanguage.queryURL(for:)` hardcodes a path layout that
only matches the Xcode-produced bundle. The editor renders as plain gray
text with no error.

Both stem from the same assumption — that the package is always built and
embedded by Xcode. Dev builds on the build machine work, which is why this is
easy to miss.

## Environment

- Package: `CodeEditLanguages` 0.1.20 (pulled transitively via
`CodeEditSourceEditor` 0.15.2).
- Build: `swift build` from the command line. **No `.xcodeproj`.** The app is
assembled into a `.app` by a custom script that copies the SwiftPM resource
bundle (`CodeEditLanguages_CodeEditLanguages.bundle`) into
`MyApp.app/Contents/Resources/`.
- macOS, Apple Silicon, current Swift toolchain.

## Defect 1 — `Bundle.module` fatalErrors in a distributed `.app`

### Root cause

The SwiftPM-generated accessor
(`.build/…/CodeEditLanguages.build/DerivedSources/resource_bundle_accessor.swift`)
probes exactly two locations:

```swift
let mainPath = Bundle.main.bundleURL.appendingPathComponent("CodeEditLanguages_CodeEditLanguages.bundle").path
let buildPath = "/Users//…/.build/arm64-apple-macosx/debug/CodeEditLanguages_CodeEditLanguages.bundle"

let preferredBundle = Bundle(path: mainPath)
guard let bundle = preferredBundle ?? Bundle(path: buildPath) else {
Swift.fatalError("could not load resource bundle: from \(mainPath) or \(buildPath)")
}
```

- `mainPath` uses `Bundle.main.**bundleURL**` — i.e. the `.app` wrapper root,
**not** `Contents/Resources`. A correctly-formed `.app` keeps resources in
`Contents/Resources`, so this candidate never matches.
- `buildPath` is a hardcoded **absolute path on the build machine**. It
resolves during local development, but obviously not on any other machine.

Result on an end user's machine: neither candidate exists →
`Swift.fatalError` → the app crashes the first time a `CodeLanguage`'s
`resourceURL` / query is accessed.

`Bundle.module` (`CodeLanguage.resourceURL`) is used here:

```swift
// CodeLanguage.swift
internal var resourceURL: URL? = Bundle.module.resourceURL
```

### Repro

1. Depend on `CodeEditLanguages` (directly or via `CodeEditSourceEditor`).
2. `swift build` (no Xcode project).
3. Assemble a `.app` and copy `CodeEditLanguages_CodeEditLanguages.bundle`
into `Contents/Resources/`.
4. Run the `.app`. First access to a language's `queryURL` → crash at the
accessor's `fatalError`.

- **Expected:** the bundle is found under `Contents/Resources`; no crash.
- **Actual:** `fatalError("could not load resource bundle …")`.

## Defect 2 — query path assumes the Xcode bundle layout

### Root cause

```swift
// CodeLanguage.swift
internal func queryURL(for highlights: String = "highlights") -> URL? {
return resourceURL?
.appendingPathComponent("Resources/tree-sitter-\(tsName)/\(highlights).scm")
}
```

The `"Resources/"` segment is unconditionally prepended. That matches the
directory nesting Xcode produces, but under the SwiftPM command-line build the
grammar files do **not** live at that relative path from the resolved
`resourceURL`, so `queryURL` points at a file that does not exist. The lookup
returns a URL that never resolves, no error is thrown, and highlighting
silently degrades to plain text.

### Repro

1. With Defect 1 worked around (bundle located), open any source file in an
editor backed by `CodeEditSourceEditor`.
2. Observe: no syntax colors; text is uniform gray.
3. Log `CodeLanguage.swift.queryURL` and `FileManager.fileExists` on the
returned path — the file is not there.

- **Expected:** the `.scm` query loads; tokens are highlighted.
- **Actual:** `queryURL` resolves to a non-existent path; queries silently
fail.

## Proposed fix

Both are small and, importantly, **change nothing for Xcode consumers** — the
new code paths only engage when the current lookup fails.

1. **Bundle resolution** — before falling back to `Bundle.module`, probe
`Bundle.main.resourceURL` (i.e. `Contents/Resources`), where a
CLI-assembled app copies the SwiftPM resource bundle:

```swift
private static let queryBundleResourceURL: URL? = {
if let bundled = Bundle.main.resourceURL?
.appendingPathComponent("CodeEditLanguages_CodeEditLanguages.bundle"),
let bundle = Bundle(url: bundled) {
return bundle.resourceURL
}
return Bundle.module.resourceURL
}()

internal var resourceURL: URL? = CodeLanguage.queryBundleResourceURL
```

2. **Layout-aware query path** — resolve against whichever layout exists on
disk, probing the nested (`Resources/…`) layout first so Xcode behavior is
byte-for-byte unchanged, then the flat layout:

```swift
guard let base = resourceURL else { return nil }
let relativePath = "tree-sitter-\(tsName)/\(highlights).scm"
let nested = base.appendingPathComponent("Resources/\(relativePath)")
if FileManager.default.fileExists(atPath: nested.path) {
return nested
}
return base.appendingPathComponent(relativePath)
```

## Offer to contribute

We are already running these two changes as a local patch against 0.1.20 and
they resolve both issues with no observed regression for Xcode-built consumers.
Per the CodeEdit contribution convention (open an issue first, ask to be
assigned), we'd be glad to submit the PR — could you assign this to us? The
patch passes SwiftLint and keeps the existing code paths as the primary
lookup. Happy to adjust the approach if you'd prefer a different shape.

Thanks for maintaining CodeEditLanguages.

Hướng dẫn đóng góp

Chưa lập chỉ mục được hướng dẫn đóng góp cho kho mã nguồn này

Hướng nghiên cứu

Start in CodeLanguage.swift, especially resourceURL and queryURL(for:), then reproduce with swift build and a hand-assembled .app containing the named resource bundle. Verify the bundle is found under Contents/Resources, both query layouts work, .scm files load for highlighting, and Xcode-built consumers retain their current behavior; run SwiftLint.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
macos, swift
Lĩnh vực
devtools
Loại issue
Lỗi
Độ khó
3/5
Thời gian dự kiến
1-2 ngày
Mức độ hoạt động
Ít trao đổi
Độ rõ ràng
Đặc tả rõ ràng
Mức phù hợp với người mới
65/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.