Skip to content

NES: split open-tab vs related-file neighbor context - #326626

Merged
Ulugbek Abdullaev (ulugbekna) merged 6 commits into
mainfrom
ulugbekna/nes-neighboring-files-context
Jul 20, 2026
Merged

Ulugbek Abdullaev (ulugbekna) merged 6 commits into
mainfrom
ulugbekna/nes-neighboring-files-context

Conversation

@ulugbekna

@ulugbekna Ulugbek Abdullaev (ulugbekna) commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Two related improvements to the xtab (NES / Next Edit Suggestions) neighboring-files context. Both hinge on distinguishing neighbor snippets that come from open tabs vs. from language-service "related" files (the non-open-tab files a language service suggests, e.g. related/typescript, related/cpp).

  1. No line numbers for related-file snippets. Snippets sourced from language-service related files now omit line numbers in the prompt, matching NES's own language-context path (appendLanguageContextSnippets, which already uses IncludeLineNumbersOption.None). Open-tab neighbor snippets are unchanged and keep their line numbers.
  2. Ability to turn off the related-files context. A new neighborFiles.includeRelatedFiles option lets us disable the completions-style related-files context entirely, since NES already has its own language-context implementation (ILanguageContextProviderService). When off, the related-files LSP computation is skipped entirely (no wasted work), leaving only open-tab neighbors.

Both default to behavior-preserving settings, so this PR is a no-op until the new config is flipped.

Motivation

NES's neighboring-files context (Jaccard-ranked snippets from Completions) overlaps with NES's own language context. This PR makes the two coexist cleanly: related-file snippets are formatted consistently with the language-context path (no line numbers), and the redundant related-files context can be switched off per experiment.

What changed

Point 1 — provenance tag + line-number formatting. Each neighbor snippet carries its provenance, decided once at the source (where the neighbor type is known) rather than re-derived from URIs:

  • neighborFiles.ts (completions-core): isRelatedNeighboringFileType(type) is a switch over the Related* enum members (robust to enum changes, no string-prefix matching). getNeighborFilesAndTraits stamps isFromRelatedFile on each related doc as it is materialized.
  • prompt.ts / selectRelevance.ts: SimilarFileInfo and ScoredSnippet gain an optional isFromRelatedFile. getSimilarSnippets propagates it (and the source uri) onto every ranked snippet and guarantees it on its return type (SnippetWithSourceInfo).
  • similarFilesContext.ts: getSnippetsForPrompt reads s.isFromRelatedFile directly — no post-ranking URI set / cross-referencing.
  • similarFilesContextService.ts: INeighborFileSnippet gains readonly isFromRelatedFile: boolean.
  • recentFilesForPrompt.ts: appendNeighborFileSnippets uses IncludeLineNumbersOption.None for related-file snippets, else the configured option.

Point 2 — toggle

  • xtabPromptOptions.ts: adds includeRelatedFiles to NeighborFilesOptions (type + VALIDATOR + DEFAULT_OPTIONS, default true).
  • configurationService.ts: new team-internal, experiment-based key chat.advanced.inlineEdits.xtabProvider.neighborFiles.includeRelatedFiles.
  • xtabProvider.ts: wires the config into the neighborFiles options and threads the flag into getSnippetsForPrompt and the fire-and-forget telemetry compute.
  • neighborFiles.ts (completions-core): getNeighborFilesAndTraits gains an includeRelatedFiles: boolean = true param; when false it returns early with open-tab neighbors only, before any related-files/LSP work.

Behavior & compatibility

  • Defaults unchanged: includeRelatedFiles defaults to true; related files continue to be included exactly as before. Line-numbering for open-tab snippets is unchanged.
  • Only new behavior is (a) related-file snippets drop line numbers, and (b) the new exp key can turn related files off.
  • Gated correctly for AI-disabled users — this is inside the existing xtab provider path and only runs when neighborFiles.enabled is on.

How to review

Suggested reading order:

  1. similarFilesContextService.ts — the new isFromRelatedFile field + the includeRelatedFiles param on the two service methods. This is the contract for everything else.
  2. neighborFiles.ts (completions-core) — isRelatedNeighboringFileType, the isFromRelatedFile stamp on related docs, and the one-line early-return in getNeighborFilesAndTraits (if (!includeRelatedFiles || featuresService.excludeRelatedFiles(...))). Confirm skipping related files still returns open-tab neighbors + empty traits.
  3. similarFiles.ts / selectRelevance.ts — the tag rides on SimilarFileInfo → each snippet; getSimilarSnippets guarantees uri + isFromRelatedFile on its return type.
  4. similarFilesContext.tsgetSnippetsForPrompt classifies purely from s.isFromRelatedFile (no URI set), so multi-root relative-path collisions can't misclassify a snippet.
  5. recentFilesForPrompt.ts — related snippets get IncludeLineNumbersOption.None; open-tab snippets keep the configured option.

Tests

  • similarFiles.test.ts (prompt): getSimilarSnippets preserves each source uri under a multi-root relativePath collision, and propagates isFromRelatedFile from source file → snippet.
  • neighborFiles.test.ts (completions-core): includeRelatedFiles=false retains open-tab neighbors, keeps traits empty, and (via a call counter on the mocked provider) proves the related-files provider is never invoked — with a positive control for the enabled path.
  • recentFilesForPrompt.spec.ts (xtab): related snippets omit line numbers; open-tab snippets keep them.

For the xtab (NES) neighboring-files context, distinguish neighbor
snippets that come from open tabs vs. language-service "related" files:

- Omit line numbers for related-file snippets, matching the NES
  language-context path; open-tab snippets keep their line numbers.
- Add a `neighborFiles.includeRelatedFiles` option (default `true`, so
  behavior is unchanged) that turns off the related-files context, since
  NES has its own language-context implementation. When off, the
  related-files LSP computation is skipped entirely.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1bb7414b-49be-41eb-b86b-bc8d7a319268
Copilot AI review requested due to automatic review settings July 20, 2026 14:30

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Splits NES neighbor context by open-tab versus language-service-related provenance.

Changes:

  • Adds related-file provenance and suppresses its line numbers.
  • Adds a configuration toggle that skips related-file discovery.
  • Adds prompt-formatting coverage.
Show a summary per file
File Description
xtabPromptOptions.ts Defines the related-files option.
configurationService.ts Registers the experiment setting.
recentFilesForPrompt.spec.ts Tests related-file formatting.
xtabProvider.ts Threads the setting through NES.
similarFilesContextService.ts Extends the service contract and snippet metadata.
recentFilesForPrompt.ts Selects line-number formatting by provenance.
similarFilesContext.ts Derives and applies related-file provenance.
neighborFiles.ts Classifies related sources and skips their computation.

Review details

  • Files reviewed: 8/8 changed files
  • Comments generated: 2
  • Review effort level: Medium

Comment thread extensions/copilot/src/extension/inlineEdits/vscode-node/similarFilesContext.ts Outdated
…iles toggle

Carry the source document's URI on ranked snippets (added to ScoredSnippet
and populated in getSimilarSnippets) so neighbor-snippet provenance no longer
relies on reconstructing a URI from relativePath. In multi-root workspaces two
documents can share a relativePath, so the previous last-wins relativePath->uri
map could misclassify an open-tab snippet as a related file and wrongly drop its
line numbers.

Add a test asserting that when includeRelatedFiles is false the open-tab
neighbors are still returned, no related-file traits are produced, and the
related-files provider is never invoked (guarding the promised LSP-work
avoidance), with a positive control proving the provider is invoked when enabled.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1bb7414b-49be-41eb-b86b-bc8d7a319268
…i-root test

Address review follow-ups:
- getSimilarSnippets now returns SnippetWithSourceUri (uri guaranteed), and
  spreads the match first so the source file's uri/relativePath always win.
- similarFilesContext drops the `s.uri ?? uri` fallback so a future propagation
  regression fails at the type level instead of silently misattributing a
  snippet to the active document.
- Add a getSimilarSnippets test covering two neighbor files that share a
  relativePath (multi-root) and asserting each snippet keeps its true uri.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1bb7414b-49be-41eb-b86b-bc8d7a319268
Address review: replace the inline nested loop that derived the related-file
URI set in SimilarFilesContextService with a named `collectRelatedFileUris`
helper co-located with `isRelatedNeighboringFileType` in neighborFiles.ts. The
call site is now a single readable line and the "which neighbor types are
related" concept lives in one place. Pure refactor, behavior unchanged.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1bb7414b-49be-41eb-b86b-bc8d7a319268
Per review: rather than building a set of related-file URIs and re-classifying
each ranked snippet by URI membership, decide the classification once where the
neighbor type is known and let it ride on the snippet.

- SimilarFileInfo/ScoredSnippet gain an optional `isFromRelatedFile` flag.
- getNeighborFilesAndTraits stamps it via `isRelatedNeighboringFileType(type)`
  when it materializes each related doc.
- getSimilarSnippets propagates the flag onto every snippet (like uri).
- SimilarFilesContextService reads `s.isFromRelatedFile` directly; the
  `relatedFileUris` set, its plumbing, and `collectRelatedFileUris` are gone.

Classification is now robust by construction (no dependence on URI-string
equality across two maps). `neighborSource` is untouched (still used by
completions telemetry/prompt). Adds a getSimilarSnippets test asserting the
flag propagates from source file to snippet.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1bb7414b-49be-41eb-b86b-bc8d7a319268
Per review: make the provenance flag non-optional where consumers actually use
it, without forcing matcher-stage snippets (which have no provenance yet) to
fabricate a value.

- ScoredSnippet.isFromRelatedFile stays optional on the base type (documented
  why: the matcher produces snippets before provenance is known), mirroring how
  `uri`/`relativePath` are handled.
- getSimilarSnippets normalizes it (`?? false`) and its return type now
  guarantees `isFromRelatedFile: boolean` (renamed SnippetWithSourceUri ->
  SnippetWithSourceInfo, which now carries both `uri` and the flag).
- SimilarFilesContextService reads `s.isFromRelatedFile` directly with no
  fallback.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1bb7414b-49be-41eb-b86b-bc8d7a319268
@ulugbekna
Ulugbek Abdullaev (ulugbekna) marked this pull request as ready for review July 20, 2026 22:41
@ulugbekna Ulugbek Abdullaev (ulugbekna) added the ~release-cherry-pick Trigger: cherry-pick this PR to the latest release branch label Jul 20, 2026
@vs-code-engineering

Copy link
Copy Markdown
Contributor

This PR will be automatically cherry-picked to release/1.130 when merged.

@ulugbekna
Ulugbek Abdullaev (ulugbekna) merged commit 395f76c into main Jul 20, 2026
29 checks passed
@ulugbekna
Ulugbek Abdullaev (ulugbekna) deleted the ulugbekna/nes-neighboring-files-context branch July 20, 2026 23:41
@vs-code-engineering vs-code-engineering Bot added this to the 1.130.0 milestone Jul 20, 2026
@vs-code-engineering vs-code-engineering Bot added release-cherry-pick Automated cherry-pick between release and main branches and removed ~release-cherry-pick Trigger: cherry-pick this PR to the latest release branch labels Jul 20, 2026
@vs-code-engineering vs-code-engineering Bot locked and limited conversation to collaborators Sep 4, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

release-cherry-pick Automated cherry-pick between release and main branches

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants