Skip to content

Fix: NES debounce and language context fetch do not honor cancellation token#4384

Merged
ulugbekna merged 2 commits intomicrosoft:mainfrom
etvorun:fix_debounce
Mar 17, 2026
Merged

Fix: NES debounce and language context fetch do not honor cancellation token#4384
ulugbekna merged 2 commits intomicrosoft:mainfrom
etvorun:fix_debounce

Conversation

@etvorun
Copy link
Contributor

@etvorun etvorun commented Mar 12, 2026

Fixes #4383

Problem

When Visual Studio cancels a Next Edit Suggestions (NES) request (e.g. because the user moved the cursor or a new request superseded the old one), the cancellation was effectively a no-op for two critical phases of the request pipeline in \XtabProvider:

  1. Debounce phase — \debounce()\ used \�wait timeout(debounceTime)\ unconditionally. Cancelling the request had no effect; the debounce timer ran to completion before the cancellation check fired.
  2. Language context fetch phase — \getLanguageContext()\ used \�wait raceTimeout(getContextPromise(), debounceTime)\ without passing the cancellation token. The fetch would block the full debounce window even when the outer request was already cancelled.

Impact on request queuing

Because cancelled requests would not yield until the debounce (and optionally the language context timeout) fully elapsed, subsequent NES requests accumulated behind them. Each queued request inherited an artificially inflated wait time, causing a visible multi-hundred-millisecond delay before later requests could start their own debounce or LLM fetch. In scenarios where VS fires rapid NES requests (e.g. caret moves after Tab acceptance) this manifested as a stall: the cancelled request's debounce blocked the pipeline, making healthy follow-on requests appear delayed or entirely unresponsive.

Fix

The debounce now resolves immediately when the cancellation token fires, instead of always waiting the full configured window.

Behavior Change

Scenario Before After
VS cancels NES request during debounce Debounce runs to completion (~100–500ms depending on config) Debounce exits immediately
VS cancels NES request during language context fetch Fetch waits for full \debounceTime\ budget Fetch exits immediately
Next request behind a cancelled one Delayed by predecessor's residual wait Starts immediately after cancellation
Simulation tests Unaffected (debounce is skipped in simulation context) Unchanged
Normal (non-cancelled) requests Unchanged Unchanged

Testing

New unit tests (\src/extension/xtab/test/node/xtabProvider.spec.ts)

Two tests were added to the \XtabProvider integration > debounce behavior\ suite:

*\cancellation during debounce exits early with GotCancelled before LLM fetch*

  • Configures a 500 ms debounce.
  • Starts \provideNextEdit\ and cancels the token after 20 ms.
  • Asserts the result is \NoNextEditReason.GotCancelled.
  • Asserts total elapsed time is well under 500 ms (debounce was interrupted).
  • Asserts \streamingFetcher.callCount === 0\ — the LLM fetch was never reached.

*\pre-cancelled token resolves without waiting for debounce*

  • Configures a 500 ms debounce.
  • Cancels the token before calling \provideNextEdit.
  • Asserts the result is \NoNextEditReason.GotCancelled.
  • Asserts total elapsed time is well under 500 ms — no blocking wait occurred.

Existing test coverage

  • Prior tests already verify that \debounce()\ is skipped entirely in simulation mode (\isInSimulationTests = true), so simulation tests remain unaffected.
  • The cancellation token is already propagated throughout \doGetNextEditWithSelection; the fix wires it into the two previously-exempt awaits in the same call chain.

Manual validation

In VS, cancel rapid successive NES requests (cursor moves, new edits) and verify via ETW / LSP logs that follow-on \NES.GetNextEdit.Start\ events are no longer delayed by the predecessor's debounce window.

@andreamah
Copy link

LGTM!

@etvorun etvorun marked this pull request as ready for review March 13, 2026 15:00
Copilot AI review requested due to automatic review settings March 13, 2026 15:00
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes cancellation handling in the Next Edit Suggestions (NES) pipeline within XtabProvider, so cancelled requests don’t continue to block during debounce or language-context collection—reducing queued-up latency for subsequent requests.

Changes:

  • Wire the request CancellationToken into the debounce wait so it resolves immediately on cancellation.
  • Wrap the language context timeout wait with cancellation, so cancellation stops waiting for the remaining time budget.
  • Add unit tests covering cancellation during debounce and pre-cancelled requests.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
src/extension/xtab/node/xtabProvider.ts Makes debounce and language-context waiting honor cancellation via raceCancellation.
src/extension/xtab/test/node/xtabProvider.spec.ts Adds unit tests asserting cancellation exits early and avoids issuing the LLM fetch.
Comments suppressed due to low confidence (1)

src/extension/xtab/node/xtabProvider.ts:556

  • getLanguageContext now stops waiting on cancellation via raceCancellation(...), but it still proceeds to compute langCtxOnTimeout and run isSnippetIgnored checks afterward. That additional async work can still delay promise resolution even after cancellation, undermining the goal of exiting immediately. Consider short-circuiting right after the await when cancellationToken.isCancellationRequested (e.g., return undefined / skip on-timeout processing when cancelled).
			const start = Date.now();
			await raceCancellation(raceTimeout(getContextPromise(), debounceTime), cancellationToken);
			const end = Date.now();

			const langCtxOnTimeout = this.langCtxService.getContextItemsOnTimeout(textDoc, ctxRequest);
			for (const item of langCtxOnTimeout) {

You can also share your feedback on Copilot code review. Take the survey.

@vs-code-engineering vs-code-engineering bot added this to the 1.112.0 milestone Mar 16, 2026
@chrmarti chrmarti assigned ulugbekna and unassigned hediet Mar 16, 2026
@etvorun
Copy link
Contributor Author

etvorun commented Mar 16, 2026

@chrmarti, @benibenj or someone else, can you please merge this PR. I do not have the rights to do that.

@benibenj benibenj added this pull request to the merge queue Mar 16, 2026
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Mar 16, 2026
@etvorun
Copy link
Contributor Author

etvorun commented Mar 16, 2026

@ulugbekna, @benibenj or anyone else, this PR was removed from merge queue due to test error "Failed to download and unzip VS Code insiders-unreleased". Any recommendations on how to resolve it?

@mjbvz mjbvz modified the milestones: 1.112.0, 1.113.0 Mar 17, 2026
@ulugbekna ulugbekna added this pull request to the merge queue Mar 17, 2026
Merged via the queue into microsoft:main with commit cf952dc Mar 17, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

NES: Debounce and language context fetch do not honor cancellation token

8 participants