Change:
From Steps to Reproduce: top of , add: code dev so that the modified ts extension is running a ts file in the dev instance of code: signature help in but then move the cursor up or down a line Bug Even though we likely no longer need the signature help result, it appears that we never actually cancel SignatureHelpProvider request
Cases where we may want to cancel the request: Selection change Document contents change Active document changes Suggestions requested
Should be fixed for the following cases: Document changes Request cancelled by user Mouse selection Re-trigger hints

Discussion:
private triggerCharactersListeners: IDisposable[]; private active: boolean; private throttledDelayer: RunOnceScheduler; <ins> private provideSignatureHelpRequest?: TPromise<boolean, any>; </ins> constructor(editor: ICodeEditor) { super();

if (!silent) { this._onCancel.fire(void 0); } <ins> if (this.provideSignatureHelpRequest) { this.provideSignatureHelpRequest.cancel(); this.provideSignatureHelpRequest = undefined; } </ins> } trigger(delay = ParameterHintsModel.DELAY): void {

} private doTrigger(): void { <del> provideSignatureHelp(this.editor.getModel(), this.editor.getPosition()) </del> <ins> if (this.provideSignatureHelpRequest) { this.provideSignatureHelpRequest.cancel(); } this.provideSignatureHelpRequest = provideSignatureHelp(this.editor.getModel(), this.editor.getPosition()) </ins> .then(null, onUnexpectedError) .then(result => { if (!result || !result.signatures || result.signatures.length === 0) {

<ins> /*--------------------------------------------------------------------------------------------- *  Copyright (c) Microsoft Corporation. All rights reserved. *  Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import URI from 'vs/base/common/uri'; import { Position } from 'vs/editor/common/core/position'; import { ITextModel } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; import { SignatureHelp, SignatureHelpProvider, SignatureHelpProviderRegistry } from 'vs/editor/common/modes'; import { MockScopeLocation, TestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; import { IStorageService, NullStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { ParameterHintsModel } from '../parameterHintsWidget'; function createMockEditor(model: TextModel): TestCodeEditor { const contextKeyService = new MockContextKeyService(); const telemetryService = NullTelemetryService; const notificationService = new TestNotificationService(); const instantiationService = new InstantiationService(new ServiceCollection( [IContextKeyService, contextKeyService], [ITelemetryService, telemetryService], [IStorageService, NullStorageService], [INotificationService, TestNotificationService] )); const editor = new TestCodeEditor(new MockScopeLocation(), {}, false, instantiationService, contextKeyService, notificationService); editor.setModel(model); return editor; } suite('ParameterHintsModel', () => { let disposables: IDisposable[] = []; setup(function () { disposables = dispose(disposables); }); test('Should cancel existing request when new request comes in', () => { const textModel = TextModel.createFromString('abc def', undefined, undefined, URI.parse('test:somefile.ttt')); disposables.push(textModel); const editor = createMockEditor(textModel); const hintsModel = new ParameterHintsModel(editor); let didRequestCancellationOf = -1; let invokeCount = 0; const longRunningProvider = new class implements SignatureHelpProvider { signatureHelpTriggerCharacters: string[] = []; provideSignatureHelp(model: ITextModel, position: Position, token: CancellationToken): SignatureHelp | Thenable<SignatureHelp> { const count = invokeCount++; token.onCancellationRequested(() => { didRequestCancellationOf = count; }); // retrigger on first request if (count === 0) { hintsModel.trigger(0); } return new Promise<SignatureHelp>(resolve => { setTimeout(() => { resolve({ signatures: [{ label: '' + count, parameters: [] }], activeParameter: 0, activeSignature: 0 }); }, 100); }); } }; disposables.push(SignatureHelpProviderRegistry.register({ scheme: 'test' }, longRunningProvider)); hintsModel.trigger(0); assert.strictEqual(-1, didRequestCancellationOf); return new Promise((resolve, reject) => hintsModel.onHint(e => { try { assert.strictEqual(0, didRequestCancellationOf); assert.strictEqual('1', e.hints.signatures[0].label); resolve(); } catch (e) { reject(e); } })); }); }); </ins>