Skip to content

feat(extensions): match file languages by filename or glob - #851

Merged
benvinegar merged 5 commits into
mainfrom
feat/file-language-matchers
Aug 26, 2026
Merged

feat(extensions): match file languages by filename or glob#851
benvinegar merged 5 commits into
mainfrom
feat/file-language-matchers

Conversation

@benvinegar

@benvinegar benvinegar commented Aug 25, 2026

Copy link
Copy Markdown
Member

Summary

  • extend hunk.registerFileLanguage with declarative exact-filename and basename/path glob matchers while preserving the existing extension-string shorthand
  • resolve selectors in Hunk with documented precedence, platform-neutral slash semantics, and protected .mts/.cts mappings
  • pin the selected language into Pierre metadata so non-extension selectors render consistently
  • replace selector generations atomically across extension reloads and restore the previous generation when a reload fails before commit
  • publish the additive contract as extension API v9 and document the new matcher forms

This provides the extension seam needed to move mappings such as Bazel/Starlark filenames out of core in a follow-up to #813 without exposing arbitrary regular expressions.

Example use cases for extension implementers

export default function activate(hunk: HunkExtensionApi) {
  // Existing shorthand: map a dotted extension, case-insensitively.
  hunk.registerFileLanguage(".bzl", "python");

  // Bare tool/config filenames at any directory depth.
  hunk.registerFileLanguage({ kind: "filename", value: "BUILD" }, "python");
  hunk.registerFileLanguage({ kind: "filename", value: "WORKSPACE" }, "python");

  // Filename families, regardless of their containing directory.
  hunk.registerFileLanguage(
    { kind: "glob", value: "Dockerfile.*", target: "basename" },
    "dockerfile",
  );

  // Generated or vendored trees, matched against the complete review path.
  hunk.registerFileLanguage(
    { kind: "glob", value: "generated/**/*.proto", target: "path" },
    "protobuf",
  );
}

This lets extensions support conventions that cannot be represented by a final extension alone: Bazel/Starlark files such as BUILD, Dockerfile variants such as Dockerfile.dev, framework-specific filename families, and files whose language depends on where they live in a repository.

String registrations remain extension-only shorthand: use { kind: "filename" } for a literal bare filename. Filename and glob selectors are case-sensitive. target: "basename" sees only the last path component, while target: "path" sees the decoded review path using / as its only separator; backslashes remain literal filename characters.

Validation

  • bun run typecheck
  • bun run lint
  • bun run deps:check
  • bun run check:docs
  • bun run check:pack
  • bun run test — 3,054 passed, 10 skipped
  • bun run test:integration — 128 passed
  • bun run test:tty-smoke — 9 passed
  • real TTY smoke run against a TypeScript diff
  • all GitHub checks, including Windows compatibility

This PR description was generated by Pi using gpt-5.6-sol

@vercel

vercel Bot commented Aug 25, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hunk-web Ready Ready Preview Aug 26, 2026 3:41am

Request Review

@greptile-apps

greptile-apps Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR expands extension file-language registration from extension suffixes to exact filenames and basename/path globs, while preserving deterministic precedence and protected TypeScript mappings.

  • Adds and publishes extension API v9 matcher types, validation, and documentation.
  • Introduces versioned, atomically replaceable selector generations with rollback during failed reloads.
  • Resolves selectors while constructing diff files and pins the selected language into Pierre metadata.
  • Adds unit and integration coverage for matching, precedence, reload replacement, rollback, and public API robustness.

Confidence Score: 5/5

The PR appears safe to merge, with no concrete blocking or independently actionable non-blocking issues identified.

Matcher validation, precedence, metadata propagation, generation replacement, and reload rollback are implemented consistently and covered across unit and host-level tests.

Important Files Changed

Filename Overview
src/core/changeset/fileLanguageLookup.ts Compiles versioned selectors lazily and implements reserved-extension, filename, glob, and longest-extension precedence.
src/core/changeset/fileLanguage.ts Replaces the prior pending-map model with immutable selector snapshots and atomic generation replacement/restoration.
src/extensions/runExtension.ts Validates and canonicalizes the new public matcher forms while preserving extension-factory failure isolation.
src/extensions/apply.ts Applies complete selector generations and continues to reject direct attempts to override reserved extensions.
src/app/sessionBootstrap.ts Captures and restores the prior selector generation when candidate session bootstrap fails.
src/ui/AppHost.tsx Integrates selector rollback into live reload commit, shutdown, and publication-failure paths.
src/core/changeset/diffFile.ts Resolves each file’s selected language and pins it into renderer metadata for non-extension matchers.
src/extension-api/types.ts Publishes the additive matcher union and advances the extension API generation to version 9.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Extension factory] --> B[Validate matcher]
  B --> C[Extension registry]
  C --> D[Replace active selector generation]
  D --> E[Load changeset]
  E --> F[Resolve filename, glob, or extension]
  F --> G[Pin language into Pierre metadata]
  E -->|Failure| H[Restore previous generation]
  G --> I[Commit review and extension generation]
Loading

Reviews (1): Last reviewed commit: "feat(extensions): match file languages b..." | Re-trigger Greptile

@benvinegar
benvinegar force-pushed the feat/file-language-matchers branch from 4357848 to a1b5fe1 Compare August 26, 2026 03:40
@benvinegar
benvinegar merged commit 4d8b000 into main Aug 26, 2026
17 of 18 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.

1 participant