Skip to content

Commit cdbf451

Browse files
colbymchenryclaude
andauthored
fix(extraction): count all file-level-tracked langs (incl .properties) as indexed (colbymchenry#544)
Completes colbymchenry#357. The no-symbol file-level class is yaml/twig/properties, but the count fix only covered yaml/twig — so a .properties-only project still printed "No files found to index" even though the files were stored. Introduce a single isFileLevelOnlyLanguage predicate (the canonical set behind the tree-sitter no-symbol branch, xml excluded since its MyBatis extractor emits a file node) and use it at both count sites and the extraction dispatch so the list can't drift. Adds .properties regression coverage for indexAll() and indexFiles(). Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
1 parent 839cf63 commit cdbf451

5 files changed

Lines changed: 57 additions & 8 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
99

1010
## [Unreleased]
1111

12+
### Fixes
13+
14+
- Indexing a project that contains only config-style files (YAML, Twig, or `.properties`) no longer misleadingly reports "No files found to index" — these files are tracked at the file level and are now counted as indexed. Thanks @luojiyin1987 (#357).
15+
1216
## [0.9.7] - 2026-05-28
1317

1418
### New Features

__tests__/extraction.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3258,6 +3258,38 @@ export function multiply(a: number, b: number): number {
32583258

32593259
cg.close();
32603260
});
3261+
3262+
it('should count file-level tracked .properties files as indexed', async () => {
3263+
fs.writeFileSync(path.join(tempDir, 'application.properties'), 'server.port=8080\n');
3264+
fs.writeFileSync(path.join(tempDir, 'log.properties'), 'log.level=INFO\n');
3265+
3266+
const cg = CodeGraph.initSync(tempDir);
3267+
const result = await cg.indexAll();
3268+
3269+
expect(result.success).toBe(true);
3270+
expect(result.filesIndexed).toBe(2);
3271+
expect(result.filesSkipped).toBe(0);
3272+
3273+
cg.close();
3274+
});
3275+
3276+
it('should count the full file-level tracked class (yaml/twig/properties) in indexFiles()', async () => {
3277+
fs.writeFileSync(path.join(tempDir, 'app.yaml'), 'name: test\n');
3278+
fs.writeFileSync(path.join(tempDir, 'view.twig'), '{{ title }}\n');
3279+
fs.writeFileSync(path.join(tempDir, 'application.properties'), 'server.port=8080\n');
3280+
3281+
const cg = CodeGraph.initSync(tempDir);
3282+
const result = await cg.indexFiles(['app.yaml', 'view.twig', 'application.properties']);
3283+
3284+
expect(result.success).toBe(true);
3285+
expect(result.filesIndexed).toBe(3);
3286+
expect(result.filesSkipped).toBe(0);
3287+
3288+
const tracked = cg.getFiles().map((f) => `${f.path}:${f.language}`).sort();
3289+
expect(tracked).toEqual(['app.yaml:yaml', 'application.properties:properties', 'view.twig:twig']);
3290+
3291+
cg.close();
3292+
});
32613293
});
32623294

32633295
describe('Path Normalization', () => {

src/extraction/grammars.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,19 @@ export function isGrammarLoaded(language: Language): boolean {
290290
return languageCache.has(language);
291291
}
292292

293+
/**
294+
* Languages tracked at the file-record level only: parsing emits zero symbol
295+
* nodes, but the file is still stored (and framework resolvers may add per-file
296+
* references later, e.g. Drupal routing yml, Spring `@Value` against
297+
* application.properties). This is the canonical set behind the no-symbol
298+
* branch in `tree-sitter.ts`; `xml` is intentionally excluded because its
299+
* MyBatis extractor emits a file node. Callers use this to count such files as
300+
* indexed rather than skipped, so it must stay in sync with that branch.
301+
*/
302+
export function isFileLevelOnlyLanguage(language: Language): boolean {
303+
return language === 'yaml' || language === 'twig' || language === 'properties';
304+
}
305+
293306
/**
294307
* Get all supported languages (those with grammar definitions).
295308
*/

src/extraction/index.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
} from '../types';
1818
import { QueryBuilder } from '../db/queries';
1919
import { extractFromSource } from './tree-sitter';
20-
import { detectLanguage, isSourceFile, isLanguageSupported, initGrammars, loadGrammarsForLanguages } from './grammars';
20+
import { detectLanguage, isSourceFile, isLanguageSupported, isFileLevelOnlyLanguage, initGrammars, loadGrammarsForLanguages } from './grammars';
2121
import { logDebug, logWarn } from '../errors';
2222
import { validatePathWithinRoot, normalizePath } from '../utils';
2323
import ignore, { Ignore } from 'ignore';
@@ -942,11 +942,11 @@ export class ExtractionOrchestrator {
942942
} else if (result.errors.some((e) => e.severity === 'error')) {
943943
filesErrored++;
944944
} else {
945-
// Files with no symbols but no errors (e.g. yaml, twig) are tracked
946-
// at the file level — count them as indexed so the CLI doesn't
947-
// misleadingly report "No files found to index".
945+
// Files with no symbols but no errors (yaml, twig, properties) are
946+
// tracked at the file level — count them as indexed so the CLI
947+
// doesn't misleadingly report "No files found to index".
948948
const lang = detectLanguage(filePath, content);
949-
if (lang === 'yaml' || lang === 'twig') {
949+
if (isFileLevelOnlyLanguage(lang)) {
950950
filesIndexed++;
951951
} else {
952952
filesSkipped++;
@@ -1117,7 +1117,7 @@ export class ExtractionOrchestrator {
11171117
filesErrored++;
11181118
} else {
11191119
const tracked = this.queries.getFileByPath(filePath);
1120-
if (tracked && (tracked.language === 'yaml' || tracked.language === 'twig')) {
1120+
if (tracked && isFileLevelOnlyLanguage(tracked.language)) {
11211121
filesIndexed++;
11221122
} else {
11231123
filesSkipped++;

src/extraction/tree-sitter.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
ExtractionError,
1616
UnresolvedReference,
1717
} from '../types';
18-
import { getParser, detectLanguage, isLanguageSupported } from './grammars';
18+
import { getParser, detectLanguage, isLanguageSupported, isFileLevelOnlyLanguage } from './grammars';
1919
import { generateNodeId, getNodeText, getChildByField, getPrecedingDocstring } from './tree-sitter-helpers';
2020
import type { LanguageExtractor, ExtractorContext } from './tree-sitter-types';
2121
import { EXTRACTORS } from './languages';
@@ -3072,7 +3072,7 @@ export function extractFromSource(
30723072
// file node so the watcher tracks it without emitting symbols.
30733073
const extractor = new MyBatisExtractor(filePath, source);
30743074
result = extractor.extract();
3075-
} else if (detectedLanguage === 'yaml' || detectedLanguage === 'twig' || detectedLanguage === 'properties') {
3075+
} else if (isFileLevelOnlyLanguage(detectedLanguage)) {
30763076
// No symbol extraction at this stage — files are tracked at the file-record
30773077
// level only. Framework extractors (Drupal routing yml, Spring `@Value`
30783078
// resolution against application.yml/application.properties) run later and

0 commit comments

Comments
 (0)