fix(extraction): preload the Objective-C grammar for C-family headers (#1628) (#1781)

Land upstream PR #1634 by @maxmilian, commit
8d398a92e30c9df27196bc90833efd674384569d.

Path-only detection classifies .h files as c, so preloading previously
covered c and cpp but missed objc selected by content-aware detection.
Preload objc alongside cpp whenever c is present. Full indexing and
changed-file reindexing now share preloadLanguagesForFiles().

Retain all four upstream unit tests and place the existing #1628 changelog
entry under Unreleased / Fixes / Symbols, tests and the viewer.

Verified fail -> pass on Linux with the reporter's repro.h as the only
source file, without a .m or .mm grammar seed. Before: Objective-C parser
initialization failure, 0 nodes, index state failed. After: Indexed 1 files,
2 nodes, 1 edge, index state complete; node CGRepro finds the class and
errors.log is absent.

Validation: npx tsc && npm run copy-assets passed; rebuilt CLI is executable.
npx vitest run __tests__/preload-languages.test.ts: 4 tests passed.

Co-authored-by: Colby McHenry <colbymchenry@users.noreply.github.com>
Co-authored-by: Max Hsu <maxmilian@gmail.com>
This commit is contained in:
Colby Mchenry
2026-09-08 11:51:02 -05:00
committed by GitHub
co-authored by Colby McHenry Max Hsu
parent ee83636acb
commit e720f6ca53
3 changed files with 69 additions and 11 deletions
+26 -11
View File
@@ -742,6 +742,30 @@ function findNestedGitRepos(absDir: string, relPrefix: string): string[] {
* scope cannot diverge from each other or from `git ls-files --exclude-standard`
* (#1728).
*/
/**
* The grammars to preload for a file set.
*
* Path-only detection calls every `.h` file C, but parse-time detection reads
* the source and can reclassify it as C++ or Objective-C (`detectLanguage`
* with a `source` argument). Workers only ever get the grammars named here, so
* a header that turns out to be Objective-C in a project with no `.m` file
* found no parser and failed with `Failed to get parser for language: objc`
* (#1628). C++ was already covered; Objective-C was not.
*/
export function preloadLanguagesForFiles(
files: string[],
overrides?: Record<string, Language>
): Language[] {
const languages = [...new Set(files.map((f) => detectLanguage(f, undefined, overrides)))];
if (languages.includes('c')) {
for (const ambiguous of ['cpp', 'objc'] as const) {
if (!languages.includes(ambiguous)) languages.push(ambiguous);
}
}
return languages;
}
export class ScopeIgnore {
private embedded: Array<{ root: string; matcher: Ignore }>;
private defaults: Ignore = defaultsOnlyIgnore();
@@ -1798,11 +1822,7 @@ export class ExtractionOrchestrator {
await new Promise(resolve => setImmediate(resolve));
// Detect needed languages and load grammars in the parse worker
const neededLanguages = [...new Set(files.map((f) => detectLanguage(f, undefined, overrides)))];
// .h files default to 'c' but may be C++ — ensure cpp grammar is loaded when c is needed
if (neededLanguages.includes('c') && !neededLanguages.includes('cpp')) {
neededLanguages.push('cpp');
}
const neededLanguages = preloadLanguagesForFiles(files, overrides);
// Parse files on a pool of worker threads (keeps the main thread free for UI
// and uses every core). Falls back to in-process parsing when the compiled
@@ -3020,12 +3040,7 @@ export class ExtractionOrchestrator {
// Load only grammars needed for changed files
if (filesToIndex.length > 0) {
const overrides = loadExtensionOverrides(this.rootDir);
const neededLanguages = [...new Set(filesToIndex.map((f) => detectLanguage(f, undefined, overrides)))];
// .h files default to 'c' but may be C++ — ensure cpp grammar is loaded
if (neededLanguages.includes('c') && !neededLanguages.includes('cpp')) {
neededLanguages.push('cpp');
}
await loadGrammarsForLanguages(neededLanguages);
await loadGrammarsForLanguages(preloadLanguagesForFiles(filesToIndex, overrides));
}
// Index changed files