@@ -10,7 +10,7 @@ import { IAuthenticationService, IExperimentationService } from '../../../lib/no
1010import { ConfigKey , IConfigurationService } from '../../../platform/configuration/common/configurationService' ;
1111import { IEnvService } from '../../../platform/env/common/envService' ;
1212import { IVSCodeExtensionContext } from '../../../platform/extContext/common/extensionContext' ;
13- import { JointCompletionsProviderStrategy } from '../../../platform/inlineEdits/common/dataTypes/jointCompletionsProviderOptions' ;
13+ import { JointCompletionsProviderStrategy , JointCompletionsProviderTriggerChangeStrategy } from '../../../platform/inlineEdits/common/dataTypes/jointCompletionsProviderOptions' ;
1414import { InlineEditRequestLogContext } from '../../../platform/inlineEdits/common/inlineEditLogContext' ;
1515import { ObservableGit } from '../../../platform/inlineEdits/common/observableGit' ;
1616import { shortenOpportunityId } from '../../../platform/inlineEdits/common/utils/utils' ;
@@ -243,7 +243,21 @@ type LastNesSuggestion = {
243243
244244class JointCompletionsProvider extends Disposable implements vscode . InlineCompletionItemProvider {
245245
246- public onDidChange ?: vscode . Event < void > | undefined ;
246+ private readonly _onDidChangeEmitter = this . _register ( new vscode . EventEmitter < void > ( ) ) ;
247+ public readonly onDidChange ?: vscode . Event < void > | undefined = this . _onDidChangeEmitter . event ;
248+
249+ private _requestsInFlight = new Set < CancellationToken > ( ) ;
250+ private _completionsRequestsInFlight = new Set < CancellationToken > ( ) ;
251+
252+ private get _isRequestInFlight ( ) : boolean {
253+ return this . _requestsInFlight . size > 0 ;
254+ }
255+
256+ private get _isCompletionsRequestInFlight ( ) : boolean {
257+ return this . _completionsRequestsInFlight . size > 0 ;
258+ }
259+
260+ private _tracer = createTracer ( [ 'NES' , 'JointCompletionsProvider' ] , ( msg ) => this . _logService . trace ( msg ) ) ;
247261
248262 //#region Model picker
249263 public readonly onDidChangeModelInfo = this . _inlineEditProvider ?. onDidChangeModelInfo ;
@@ -261,15 +275,42 @@ class JointCompletionsProvider extends Disposable implements vscode.InlineComple
261275 @ILogService private readonly _logService : ILogService ,
262276 ) {
263277 super ( ) ;
264- this . onDidChange = _inlineEditProvider ?. onDidChange ;
278+
279+ const disp = this . _inlineEditProvider ?. onDidChange ?.( ( ) => {
280+ const strategy = this . _configService . getExperimentBasedConfig ( ConfigKey . TeamInternal . InlineEditsJointCompletionsProviderTriggerChangeStrategy , this . _expService ) ;
281+ switch ( strategy ) {
282+ case JointCompletionsProviderTriggerChangeStrategy . AlwaysTrigger :
283+ break ;
284+ case JointCompletionsProviderTriggerChangeStrategy . NoTriggerOnRequestInFlight :
285+ if ( this . _isRequestInFlight ) {
286+ this . _tracer . trace ( 'Skipping onDidChange event firing because request is in flight' ) ;
287+ return ;
288+ }
289+ break ;
290+ case JointCompletionsProviderTriggerChangeStrategy . NoTriggerOnCompletionsRequestInFlight :
291+ if ( this . _isCompletionsRequestInFlight ) {
292+ this . _tracer . trace ( 'Skipping onDidChange event firing because completions request is in flight' ) ;
293+ return ;
294+ }
295+ break ;
296+ default :
297+ assertNever ( strategy ) ;
298+ }
299+ this . _tracer . trace ( 'Firing onDidChange event' ) ;
300+ this . _onDidChangeEmitter . fire ( ) ;
301+ } ) ;
302+ if ( disp ) {
303+ this . _register ( disp ) ;
304+ }
305+
265306 softAssert (
266307 _completionsProvider ?. onDidChange === undefined ,
267308 'CompletionsProvider does not implement onDidChange'
268309 ) ;
269310 }
270311
271312 public async provideInlineCompletionItems ( document : vscode . TextDocument , position : vscode . Position , context : vscode . InlineCompletionContext , token : vscode . CancellationToken ) : Promise < SingularCompletionList | undefined > {
272- const tracer = createTracer ( [ 'JointCompletionsProvider' , shortenOpportunityId ( context . requestUuid ) , 'provideInlineCompletionItems' ] , ( msg ) => this . _logService . trace ( msg ) ) ;
313+ const tracer = this . _tracer . sub ( [ shortenOpportunityId ( context . requestUuid ) , 'provideInlineCompletionItems' ] ) ;
273314
274315 const strategy = this . _configService . getExperimentBasedConfig ( ConfigKey . TeamInternal . InlineEditsJointCompletionsProviderStrategy , this . _expService ) ;
275316
@@ -290,6 +331,13 @@ class JointCompletionsProvider extends Disposable implements vscode.InlineComple
290331 const completionsCts = new CancellationTokenSource ( token ) ;
291332 const nesCts = new CancellationTokenSource ( token ) ;
292333
334+ this . _requestsInFlight . add ( token ) ;
335+ const disp1 = token . onCancellationRequested ( ( ) => {
336+ tracer . trace ( `invocation #${ invocationId } : request in flight: false -- due to cancellation` ) ;
337+ this . _requestsInFlight . delete ( token ) ;
338+ } ) ;
339+ tracer . trace ( `invocation #${ invocationId } started; request in flight: true` ) ;
340+
293341 let saveLastNesSuggestion : null | LastNesSuggestion = null ;
294342 try {
295343 const docSnapshot = new StringText ( document . getText ( ) ) ;
@@ -329,6 +377,11 @@ class JointCompletionsProvider extends Disposable implements vscode.InlineComple
329377
330378 return list ;
331379 } finally {
380+ if ( ! token . isCancellationRequested ) {
381+ tracer . trace ( `invocation #${ invocationId } : request in flight: false -- due to provider finishing` ) ;
382+ this . _requestsInFlight . delete ( token ) ;
383+ }
384+ disp1 . dispose ( ) ;
332385
333386 // Only save the last NES suggestion if this is the latest invocation
334387 if ( invocationId === this . provideInlineCompletionItemsInvocationCount ) {
@@ -465,13 +518,27 @@ class JointCompletionsProvider extends Disposable implements vscode.InlineComple
465518 private _invokeCompletionsProvider ( tracer : ITracer , document : vscode . TextDocument , position : vscode . Position , context : vscode . InlineCompletionContext , tokens : { coreToken : CancellationToken ; completionsCts : CancellationTokenSource ; nesCts : CancellationTokenSource } , sw : StopWatch ) {
466519 let completionsP : Promise < vscode . InlineCompletionList | undefined > | undefined ;
467520 if ( this . _completionsProvider ) {
468- tracer . trace ( `- requesting completions provideInlineCompletionItems` ) ;
469- completionsP = this . _completionsProvider . provideInlineCompletionItems ( document , position , context , tokens . completionsCts . token ) ;
470- completionsP . then ( ( completionsR ) => {
471- tracer . trace ( `got completions response in ${ sw . elapsed ( ) } ms -- ${ completionsR === undefined ? 'undefined' : `with ${ completionsR . items . length } items` } ` ) ;
472- } ) . catch ( ( e ) => {
473- tracer . trace ( `completions provider errored after ${ sw . elapsed ( ) } ms -- ${ errors . toString ( errors . fromUnknown ( e ) ) } ` ) ;
474- } ) ;
521+ this . _completionsRequestsInFlight . add ( tokens . completionsCts . token ) ;
522+ const disp = tokens . completionsCts . token . onCancellationRequested ( ( ) => this . _completionsRequestsInFlight . delete ( tokens . completionsCts . token ) ) ;
523+ const cleanup = ( ) => {
524+ this . _completionsRequestsInFlight . delete ( tokens . completionsCts . token ) ;
525+ disp . dispose ( ) ;
526+ } ;
527+ try { // in case the provider throws synchronously
528+ tracer . trace ( `- requesting completions provideInlineCompletionItems` ) ;
529+ completionsP = this . _completionsProvider . provideInlineCompletionItems ( document , position , context , tokens . completionsCts . token ) ;
530+ completionsP . then ( ( completionsR ) => {
531+ tracer . trace ( `got completions response in ${ sw . elapsed ( ) } ms -- ${ completionsR === undefined ? 'undefined' : `with ${ completionsR . items . length } items` } ` ) ;
532+ } ) . catch ( ( e ) => {
533+ tracer . trace ( `completions provider errored after ${ sw . elapsed ( ) } ms -- ${ errors . toString ( errors . fromUnknown ( e ) ) } ` ) ;
534+ } ) . finally ( ( ) => {
535+ cleanup ( ) ;
536+ } ) ;
537+ } catch ( e ) {
538+ cleanup ( ) ;
539+ tracer . trace ( `completions provider threw synchronously after ${ sw . elapsed ( ) } ms -- ${ errors . toString ( errors . fromUnknown ( e ) ) } ` ) ;
540+ throw e ;
541+ }
475542 } else {
476543 tracer . trace ( `- no completions provider` ) ;
477544 completionsP = undefined ;
0 commit comments