Skip to content

feat(indexing): map custom extensions to languages via EXTENSION_LANGUAGE_MAP#79

Merged
giancarloerra merged 2 commits into
mainfrom
feat/extension-language-map
Jun 28, 2026
Merged

feat(indexing): map custom extensions to languages via EXTENSION_LANGUAGE_MAP#79
giancarloerra merged 2 commits into
mainfrom
feat/extension-language-map

Conversation

@giancarloerra

@giancarloerra giancarloerra commented Jun 28, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #77. EXTRA_EXTENSIONS lets you index unknown extensions, but only as plaintext, so they lose AST chunking, symbol extraction, and the call graph. EXTENSION_LANGUAGE_MAP lets you map a custom extension to a real language so it gets full language treatment end to end. The motivating case is a PHP codebase whose files are foo.class.inc: EXTENSION_LANGUAGE_MAP=.inc:php makes those files first-class PHP.

This implements the reporter's flexible-override option rather than hardcoding .inc → php globally, because .inc is ambiguous across ecosystems (PHP includes, but also asm/SQL/template includes) and a global default would misparse non-PHP .inc files for everyone.

Changes

  • New env var EXTENSION_LANGUAGE_MAP (format .inc:php,.module:php). Parsed in constants.ts into a map of input extension → the target language's canonical extension.
  • Vocabulary subtlety handled by design. The label map (getLanguageFromExtension) and the grammar map (getAstGrepLang) deliberately use different vocabularies (shell vs bash, Lang enums for JS/TS/HTML/CSS). Rather than re-encode them, each override resolves through the target's canonical extension and both functions look that up in their existing maps, so label and grammar are always consistent. A new optional override parameter on both functions (defaulting to the global map) keeps them pure and unit-testable.
  • End-to-end coverage. Mapped extensions are recognised by isIndexableFile in both the indexer and the watcher, so they are auto-discovered and trigger live incremental updates without also being listed in EXTRA_EXTENSIONS. The code graph picks them up automatically once getAstGrepLang returns a grammar for them.
  • Validation, loud not silent. Targets with no AST grammar (e.g. the reporter's .foo:bar) and malformed entries are rejected and surfaced via a single startup warning listing the ignored entries.
  • Overrides built-ins too (e.g. .h:cpp to treat headers as C++).
  • README: env-table entry plus a pointer in the custom-extensions feature bullet.

Type of change

  • New feature (non-breaking change that adds functionality)
  • Documentation update

Testing

  • Unit tests pass (npm run test:unit): 866/866, including 20 new
  • TypeScript compiles cleanly (npx tsc --noEmit)
  • New tests added for new/changed functionality

New tests cover the parser (valid mappings, extension normalization, language aliases like c++/c#/golang, rejection of grammarless targets and malformed entries, last-value-wins on duplicates), resolution through the override for both getLanguageFromExtension and getAstGrepLang (including the Lang-enum case), and isIndexableFile recognising a mapped extension. Existing extension/indexability tests stay green, pinning unchanged default behavior.

Backward compatibility

Additive. With EXTENSION_LANGUAGE_MAP unset, behavior is byte-identical (the previously-inline extension maps were only hoisted to module constants; defaults unchanged). No new dependency, no data migration. A mapped extension only changes how that extension is extracted going forward; on re-index a changed file's chunks/symbols are replaced per-file.

Naming note

I named the variable EXTENSION_LANGUAGE_MAP (clearer, parallels EXTRA_EXTENSIONS) rather than the reporter's suggested LANGUAGE_MAP_OVERRIDE. Happy to rename if preferred.

Related issues

Closes #77

Summary by CodeRabbit

  • New Features
    • Added EXTENSION_LANGUAGE_MAP to map non-standard file extensions to supported languages for full language-aware indexing.
    • Mapped extensions are now treated as indexable for both initial indexing and incremental watcher updates.
  • Bug Fixes
    • Startup now warns about ignored/invalid extension-to-language entries instead of silently continuing.
  • Documentation
    • Updated README to explain EXTENSION_LANGUAGE_MAP, precedence over built-ins, and plaintext behavior for extra extensions.
  • Tests
    • Expanded unit coverage for extension mapping overrides, parsing, AST-grammar resolution, and indexability behavior.

…UAGE_MAP (#77)

EXTRA_EXTENSIONS indexes unknown extensions as plaintext, losing AST
chunking, symbols, and the call graph. EXTENSION_LANGUAGE_MAP lets you
map an extension to a real language (e.g. .inc:php) so it gets full
language treatment end to end.

Format `.inc:php,.module:php`. Each entry resolves to the target
language's canonical extension, so both the language label
(getLanguageFromExtension) and the AST grammar (getAstGrepLang) come
from the existing maps and stay consistent, without re-encoding their
different vocabularies (shell vs bash, Lang enums for JS/TS/HTML/CSS).
Mapped extensions are auto-discovered (indexer + watcher) without also
needing EXTRA_EXTENSIONS, and can override built-ins (e.g. .h:cpp).
Targets with no AST grammar (the reporter's .foo:bar) and malformed
entries are rejected and warned about at startup.

Additive and backward compatible: with the var unset, behavior is
unchanged; no new dependency; no data migration.
@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1d05c564-9a9f-437c-a83f-a754fdee2a43

📥 Commits

Reviewing files that changed from the base of the PR and between 1c5ddf5 and c2c616f.

📒 Files selected for processing (5)
  • README.md
  • src/constants.ts
  • src/services/code-graph.ts
  • tests/unit/constants.test.ts
  • tests/unit/indexer.test.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • README.md
  • tests/unit/constants.test.ts
  • src/services/code-graph.ts
  • tests/unit/indexer.test.ts
  • src/constants.ts

📝 Walkthrough

Walkthrough

Adds EXTENSION_LANGUAGE_MAP support for mapping custom file extensions to supported languages, with parsing, startup validation, language-resolution overrides, indexer/watcher integration, tests, and README updates.

Changes

EXTENSION_LANGUAGE_MAP Feature

Layer / File(s) Summary
Extension/language lookup tables and parser
src/constants.ts
Adds shared extension and language lookup tables, parses EXTENSION_LANGUAGE_MAP into override and invalid-entry results, and updates language lookup to accept an override map.
getAstGrepLang override and shared lookup table
src/services/code-graph.ts
Extracts the AST-grep extension table and updates getAstGrepLang to resolve extensions through the EXTENSION_LANGUAGE_MAP override before AST-grep language lookup.
isIndexableFile integration in indexer and watcher
src/services/indexer.ts, src/services/watcher.ts
Indexer and watcher isIndexableFile now accept extensions present in EXTENSION_LANGUAGE_MAP as indexable, expanding which files trigger language-aware indexing and incremental updates.
Startup warning for invalid EXTENSION_LANGUAGE_MAP entries
src/index.ts
main() imports EXTENSION_LANGUAGE_MAP_INVALID and logs a warning for malformed entries or unsupported target languages found during parsing.
Tests and README documentation
tests/unit/constants.test.ts, tests/unit/indexer.test.ts, README.md
Extends unit coverage for override parsing and lookup behavior, and updates README feature and environment-variable documentation for EXTENSION_LANGUAGE_MAP.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 Hop-hop, a mapping came to play,
.inc found PHP in a brighter way.
Parsers and watchers now march in tune,
with warnings at startup under the moon.
Full-language hops, by rabbit decree! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: mapping custom extensions to languages via EXTENSION_LANGUAGE_MAP.
Description check ✅ Passed The description covers the required summary, changes, type, testing, and related issues sections, with only the checklist section left incomplete.
Linked Issues check ✅ Passed The PR satisfies #77 by adding an env-based extension-to-language mapping that enables full language-aware indexing for custom extensions.
Out of Scope Changes check ✅ Passed The README updates, parser, indexer, watcher, code graph changes, and tests all directly support the extension-to-language mapping feature.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/extension-language-map

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 markdownlint-cli2 (0.22.1)
README.md

markdownlint-cli2 wrapper config was not available before execution


Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@README.md`:
- Line 258: The README text for custom file extensions is too broad and should
not imply full language-aware processing for EXTRA_EXTENSIONS. Update the
wording around EXTRA_EXTENSIONS and extraExtensions so it clearly states they
only add plaintext indexing / leaf-node coverage for indexing and code graph,
while full AST chunking, symbols, and call graph behavior comes from
EXTENSION_LANGUAGE_MAP. Keep the guidance in the same section so readers can
distinguish the two paths.

In `@src/constants.ts`:
- Around line 354-360: `getLanguageFromExtension()` and `getAstGrepLang()` are
using unnormalized extension keys while `parseExtensionLanguageMap()` stores
lowercase overrides, so lookups like uppercase extensions can miss valid
mappings. Update the lookup logic in those symbols to normalize the incoming
extension with `toLowerCase()` before consulting the override map, and keep the
same normalization path in `getAstGrepLang()` so language and grammar resolution
stay consistent.

In `@tests/unit/indexer.test.ts`:
- Around line 133-140: The test for isIndexableFile is mutating shared
EXTENSION_LANGUAGE_MAP state and then unconditionally deleting the .inc entry,
which can break the baseline configuration. In the indexer.test.ts case around
the EXTENSION_LANGUAGE_MAP.set(".inc", ".php") setup, capture the prior value
for ".inc" before overriding it, and in the finally block restore that original
entry instead of always deleting it. Keep the existing isIndexableFile
assertions so the test still verifies temporary override behavior without
altering shared defaults.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c3bd2588-d496-4f97-8ec2-99ff8088d653

📥 Commits

Reviewing files that changed from the base of the PR and between 0dc244e and 1c5ddf5.

📒 Files selected for processing (8)
  • README.md
  • src/constants.ts
  • src/index.ts
  • src/services/code-graph.ts
  • src/services/indexer.ts
  • src/services/watcher.ts
  • tests/unit/constants.test.ts
  • tests/unit/indexer.test.ts

Comment thread README.md Outdated
Comment thread src/constants.ts
Comment thread tests/unit/indexer.test.ts Outdated
Address CodeRabbit review:
- getLanguageFromExtension/getAstGrepLang lowercase the extension before
  consulting the override map, so an uppercase ext still matches the
  lowercased keys (production callers already lowercase; .R collapses to
  .r with the same value, so no other behavior changes).
- README: clarify EXTRA_EXTENSIONS indexes as plaintext / leaf nodes,
  versus EXTENSION_LANGUAGE_MAP's full language treatment.
- Make the isIndexableFile override test hermetic (save/restore the prior
  map entry, assert against the captured baseline).
@giancarloerra giancarloerra self-assigned this Jun 28, 2026
@giancarloerra giancarloerra merged commit e1f968b into main Jun 28, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Allow additional filetype->language mappings

1 participant