Skip to content

Commit a64ca74

Browse files
committed
Merge multiple triggers for parameter hints instead of always using latest
Fixes microsoft#82825
1 parent 34e6f94 commit a64ca74

2 files changed

Lines changed: 78 additions & 8 deletions

File tree

src/vs/editor/contrib/parameterHints/parameterHintsModel.ts

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export class ParameterHintsModel extends Disposable {
5555
private readonly editor: ICodeEditor;
5656
private triggerOnType = false;
5757
private _state: ParameterHintState.State = ParameterHintState.Default;
58+
private _pendingTriggers: TriggerContext[] = [];
5859
private readonly _lastSignatureHelpResult = this._register(new MutableDisposable<modes.SignatureHelpResult>());
5960
private triggerChars = new CharacterSet();
6061
private retriggerChars = new CharacterSet();
@@ -109,13 +110,12 @@ export class ParameterHintsModel extends Disposable {
109110
}
110111

111112
const triggerId = ++this.triggerId;
112-
this.throttledDelayer.trigger(
113-
() => this.doTrigger({
114-
triggerKind: context.triggerKind,
115-
triggerCharacter: context.triggerCharacter,
116-
isRetrigger: this.state.type === ParameterHintState.Type.Active || this.state.type === ParameterHintState.Type.Pending,
117-
activeSignatureHelp: this.state.type === ParameterHintState.Type.Active ? this.state.hints : undefined
118-
}, triggerId), delay).then(undefined, onUnexpectedError);
113+
114+
this._pendingTriggers.push(context);
115+
this.throttledDelayer.trigger(() => {
116+
return this.doTrigger(triggerId);
117+
}, delay)
118+
.catch(onUnexpectedError);
119119
}
120120

121121
public next(): void {
@@ -165,9 +165,26 @@ export class ParameterHintsModel extends Disposable {
165165
this._onChangedHints.fire(this.state.hints);
166166
}
167167

168-
private doTrigger(triggerContext: modes.SignatureHelpContext, triggerId: number): Promise<boolean> {
168+
private doTrigger(triggerId: number): Promise<boolean> {
169+
const isRetrigger = this.state.type === ParameterHintState.Type.Active || this.state.type === ParameterHintState.Type.Pending;
170+
const activeSignatureHelp = this.state.type === ParameterHintState.Type.Active ? this.state.hints : undefined;
171+
169172
this.cancel(true);
170173

174+
if (this._pendingTriggers.length === 0) {
175+
return Promise.resolve(false);
176+
}
177+
178+
const context: TriggerContext = this._pendingTriggers.reduce(mergeTriggerContexts);
179+
this._pendingTriggers = [];
180+
181+
const triggerContext = {
182+
triggerKind: context.triggerKind,
183+
triggerCharacter: context.triggerCharacter,
184+
isRetrigger: isRetrigger,
185+
activeSignatureHelp: activeSignatureHelp
186+
};
187+
171188
if (!this.editor.hasModel()) {
172189
return Promise.resolve(false);
173190
}
@@ -284,3 +301,19 @@ export class ParameterHintsModel extends Disposable {
284301
super.dispose();
285302
}
286303
}
304+
305+
function mergeTriggerContexts(previous: TriggerContext, current: TriggerContext) {
306+
switch (current.triggerKind) {
307+
case modes.SignatureHelpTriggerKind.Invoke:
308+
// Invoke overrides previous triggers.
309+
return current;
310+
311+
case modes.SignatureHelpTriggerKind.ContentChange:
312+
// Ignore content changes triggers
313+
return previous;
314+
315+
case modes.SignatureHelpTriggerKind.TriggerCharacter:
316+
default:
317+
return current;
318+
}
319+
}

src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,43 @@ suite('ParameterHintsModel', () => {
425425
assert.strictEqual(secondHint.activeSignature, 1);
426426
assert.strictEqual(secondHint.signatures[0].parameters[0].label, paramterLabel);
427427
});
428+
429+
test('Quick typing should use the first trigger character', async () => {
430+
const editor = createMockEditor('');
431+
const model = new ParameterHintsModel(editor, 50);
432+
disposables.add(model);
433+
434+
const triggerCharacter = 'a';
435+
436+
let invokeCount = 0;
437+
disposables.add(modes.SignatureHelpProviderRegistry.register(mockFileSelector, new class implements modes.SignatureHelpProvider {
438+
signatureHelpTriggerCharacters = [triggerCharacter];
439+
signatureHelpRetriggerCharacters = [];
440+
441+
provideSignatureHelp(_model: ITextModel, _position: Position, _token: CancellationToken, context: modes.SignatureHelpContext): modes.SignatureHelpResult | Promise<modes.SignatureHelpResult> {
442+
try {
443+
++invokeCount;
444+
445+
if (invokeCount === 1) {
446+
assert.strictEqual(context.triggerKind, modes.SignatureHelpTriggerKind.TriggerCharacter);
447+
assert.strictEqual(context.triggerCharacter, triggerCharacter);
448+
} else {
449+
assert.fail('Unexpected invoke');
450+
}
451+
452+
return emptySigHelpResult;
453+
} catch (err) {
454+
console.error(err);
455+
throw err;
456+
}
457+
}
458+
}));
459+
460+
editor.trigger('keyboard', Handler.Type, { text: triggerCharacter });
461+
editor.trigger('keyboard', Handler.Type, { text: 'x' });
462+
463+
await getNextHint(model);
464+
});
428465
});
429466

430467
function getNextHint(model: ParameterHintsModel) {

0 commit comments

Comments
 (0)