Skip to content

Commit 5e3c96b

Browse files
Implemented support for trigger characters in signature help.
1 parent 61fb222 commit 5e3c96b

16 files changed

+109
-56
lines changed

src/harness/fourslash.ts

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1435,16 +1435,15 @@ Actual: ${stringify(fullActual)}`);
14351435
}
14361436
}
14371437

1438-
public verifyNoSignatureHelp(markers: ReadonlyArray<string>) {
1438+
public verifyNoSignatureHelp(triggerCharacter: ts.SignatureHelpTriggerCharacter | undefined, markers: ReadonlyArray<string>) {
14391439
if (markers.length) {
14401440
for (const marker of markers) {
14411441
this.goToMarker(marker);
1442-
this.verifyNoSignatureHelp(ts.emptyArray);
1442+
this.verifyNoSignatureHelp(triggerCharacter, ts.emptyArray);
14431443
}
14441444
return;
14451445
}
1446-
1447-
const actual = this.getSignatureHelp();
1446+
const actual = this.getSignatureHelp({ triggerCharacter });
14481447
if (actual) {
14491448
this.raiseError(`Expected no signature help, but got "${stringify(actual)}"`);
14501449
}
@@ -1465,7 +1464,7 @@ Actual: ${stringify(fullActual)}`);
14651464
}
14661465

14671466
private verifySignatureHelpWorker(options: FourSlashInterface.VerifySignatureHelpOptions) {
1468-
const help = this.getSignatureHelp()!;
1467+
const help = this.getSignatureHelp({ triggerCharacter: options.triggerCharacter })!;
14691468
const selectedItem = help.items[help.selectedItemIndex];
14701469
// Argument index may exceed number of parameters
14711470
const currentParameter = selectedItem.parameters[help.argumentIndex] as ts.SignatureHelpParameter | undefined;
@@ -1507,6 +1506,7 @@ Actual: ${stringify(fullActual)}`);
15071506

15081507
const allKeys: ReadonlyArray<keyof FourSlashInterface.VerifySignatureHelpOptions> = [
15091508
"marker",
1509+
"triggerCharacter",
15101510
"overloadsCount",
15111511
"docComment",
15121512
"text",
@@ -1733,7 +1733,7 @@ Actual: ${stringify(fullActual)}`);
17331733
}
17341734

17351735
public printCurrentParameterHelp() {
1736-
const help = this.languageService.getSignatureHelpItems(this.activeFile.fileName, this.currentCaretPosition);
1736+
const help = this.languageService.getSignatureHelpItems(this.activeFile.fileName, this.currentCaretPosition, /*options*/ undefined);
17371737
Harness.IO.log(stringify(help));
17381738
}
17391739

@@ -1774,12 +1774,14 @@ Actual: ${stringify(fullActual)}`);
17741774
}
17751775

17761776
public printCurrentSignatureHelp() {
1777-
const help = this.getSignatureHelp()!;
1777+
const help = this.getSignatureHelp(ts.emptyOptions)!;
17781778
Harness.IO.log(stringify(help.items[help.selectedItemIndex]));
17791779
}
17801780

1781-
private getSignatureHelp(): ts.SignatureHelpItems | undefined {
1782-
return this.languageService.getSignatureHelpItems(this.activeFile.fileName, this.currentCaretPosition);
1781+
private getSignatureHelp({ triggerCharacter }: FourSlashInterface.VerifySignatureHelpOptions): ts.SignatureHelpItems | undefined {
1782+
return this.languageService.getSignatureHelpItems(this.activeFile.fileName, this.currentCaretPosition, {
1783+
triggerCharacter
1784+
});
17831785
}
17841786

17851787
public printCompletionListMembers(preferences: ts.UserPreferences | undefined) {
@@ -1875,13 +1877,13 @@ Actual: ${stringify(fullActual)}`);
18751877
offset++;
18761878

18771879
if (highFidelity) {
1878-
if (ch === "(" || ch === ",") {
1880+
if (ch === "(" || ch === "," || ch === "<") {
18791881
/* Signature help*/
1880-
this.languageService.getSignatureHelpItems(this.activeFile.fileName, offset);
1882+
this.languageService.getSignatureHelpItems(this.activeFile.fileName, offset, { triggerCharacter: ch });
18811883
}
18821884
else if (prevChar === " " && /A-Za-z_/.test(ch)) {
18831885
/* Completions */
1884-
this.languageService.getCompletionsAtPosition(this.activeFile.fileName, offset, ts.defaultPreferences);
1886+
this.languageService.getCompletionsAtPosition(this.activeFile.fileName, offset, ts.emptyOptions);
18851887
}
18861888

18871889
if (i % checkCadence === 0) {
@@ -2514,7 +2516,7 @@ Actual: ${stringify(fullActual)}`);
25142516
`Expected '${fixId}'. Available action ids: ${ts.mapDefined(this.getCodeFixes(this.activeFile.fileName), a => a.fixId)}`);
25152517
ts.Debug.assertEqual(fixWithId!.fixAllDescription, fixAllDescription);
25162518

2517-
const { changes, commands } = this.languageService.getCombinedCodeFix({ type: "file", fileName: this.activeFile.fileName }, fixId, this.formatCodeSettings, ts.defaultPreferences);
2519+
const { changes, commands } = this.languageService.getCombinedCodeFix({ type: "file", fileName: this.activeFile.fileName }, fixId, this.formatCodeSettings, ts.emptyOptions);
25182520
assert.deepEqual<ReadonlyArray<{}> | undefined>(commands, expectedCommands);
25192521
assert(changes.every(c => c.fileName === this.activeFile.fileName), "TODO: support testing codefixes that touch multiple files");
25202522
this.applyChanges(changes);
@@ -2594,7 +2596,7 @@ Actual: ${stringify(fullActual)}`);
25942596
* Rerieves a codefix satisfying the parameters, or undefined if no such codefix is found.
25952597
* @param fileName Path to file where error should be retrieved from.
25962598
*/
2597-
private getCodeFixes(fileName: string, errorCode?: number, preferences: ts.UserPreferences = ts.defaultPreferences): ts.CodeFixAction[] {
2599+
private getCodeFixes(fileName: string, errorCode?: number, preferences: ts.UserPreferences = ts.emptyOptions): ts.CodeFixAction[] {
25982600
const diagnosticsForCodeFix = this.getDiagnostics(fileName, /*includeSuggestions*/ true).map(diagnostic => ({
25992601
start: diagnostic.start,
26002602
length: diagnostic.length,
@@ -3072,7 +3074,7 @@ Actual: ${stringify(fullActual)}`);
30723074
this.raiseError(`Expected action description to be ${JSON.stringify(actionDescription)}, got: ${JSON.stringify(action.description)}`);
30733075
}
30743076

3075-
const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, this.formatCodeSettings, range, refactorName, actionName, ts.defaultPreferences)!;
3077+
const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, this.formatCodeSettings, range, refactorName, actionName, ts.emptyOptions)!;
30763078
for (const edit of editInfo.edits) {
30773079
this.applyEdits(edit.fileName, edit.textChanges, /*isFormattingEdit*/ false);
30783080
}
@@ -3124,7 +3126,7 @@ Actual: ${stringify(fullActual)}`);
31243126
const action = ts.first(refactor.actions);
31253127
assert(action.name === "Move to a new file" && action.description === "Move to a new file");
31263128

3127-
const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, this.formatCodeSettings, range, refactor.name, action.name, options.preferences || ts.defaultPreferences)!;
3129+
const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, this.formatCodeSettings, range, refactor.name, action.name, options.preferences || ts.emptyOptions)!;
31283130
this.testNewFileContents(editInfo.edits, options.newFileContents);
31293131
}
31303132

@@ -3162,14 +3164,14 @@ Actual: ${stringify(fullActual)}`);
31623164
formattingOptions = formattingOptions || this.formatCodeSettings;
31633165
const markerPos = this.getMarkerByName(markerName).position;
31643166

3165-
const applicableRefactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, markerPos, ts.defaultPreferences);
3167+
const applicableRefactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, markerPos, ts.emptyOptions);
31663168
const applicableRefactorToApply = ts.find(applicableRefactors, refactor => refactor.name === refactorNameToApply);
31673169

31683170
if (!applicableRefactorToApply) {
31693171
this.raiseError(`The expected refactor: ${refactorNameToApply} is not available at the marker location.`);
31703172
}
31713173

3172-
const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, formattingOptions, markerPos, refactorNameToApply, actionName, ts.defaultPreferences)!;
3174+
const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, formattingOptions, markerPos, refactorNameToApply, actionName, ts.emptyOptions)!;
31733175

31743176
for (const edit of editInfo.edits) {
31753177
this.applyEdits(edit.fileName, edit.textChanges, /*isFormattingEdit*/ false);
@@ -3358,11 +3360,11 @@ Actual: ${stringify(fullActual)}`);
33583360
}
33593361

33603362
public getEditsForFileRename(options: FourSlashInterface.GetEditsForFileRenameOptions): void {
3361-
const changes = this.languageService.getEditsForFileRename(options.oldPath, options.newPath, this.formatCodeSettings, ts.defaultPreferences);
3363+
const changes = this.languageService.getEditsForFileRename(options.oldPath, options.newPath, this.formatCodeSettings, ts.emptyOptions);
33623364
this.testNewFileContents(changes, options.newFileContents);
33633365
}
33643366

3365-
private getApplicableRefactors(positionOrRange: number | ts.TextRange, preferences = ts.defaultPreferences): ReadonlyArray<ts.ApplicableRefactorInfo> {
3367+
private getApplicableRefactors(positionOrRange: number | ts.TextRange, preferences = ts.emptyOptions): ReadonlyArray<ts.ApplicableRefactorInfo> {
33663368
return this.languageService.getApplicableRefactors(this.activeFile.fileName, positionOrRange, preferences) || ts.emptyArray;
33673369
}
33683370
}
@@ -4038,7 +4040,11 @@ namespace FourSlashInterface {
40384040
}
40394041

40404042
public noSignatureHelp(...markers: string[]): void {
4041-
this.state.verifyNoSignatureHelp(markers);
4043+
this.state.verifyNoSignatureHelp(/*triggerCharacter*/ undefined, markers);
4044+
}
4045+
4046+
public noSignatureHelpForTriggerCharacter(triggerCharacter: ts.SignatureHelpTriggerCharacter, ...markers: string[]): void {
4047+
this.state.verifyNoSignatureHelp(triggerCharacter, markers);
40424048
}
40434049

40444050
public signatureHelp(...options: VerifySignatureHelpOptions[]): void {
@@ -4765,6 +4771,7 @@ namespace FourSlashInterface {
47654771
readonly isVariadic?: boolean;
47664772
/** @default ts.emptyArray */
47674773
readonly tags?: ReadonlyArray<ts.JSDocTagInfo>;
4774+
readonly triggerCharacter?: ts.SignatureHelpTriggerCharacter;
47684775
}
47694776

47704777
export type ArrayOrSingle<T> = T | ReadonlyArray<T>;

src/harness/harnessLanguageService.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -434,8 +434,8 @@ namespace Harness.LanguageService {
434434
getBreakpointStatementAtPosition(fileName: string, position: number): ts.TextSpan {
435435
return unwrapJSONCallResult(this.shim.getBreakpointStatementAtPosition(fileName, position));
436436
}
437-
getSignatureHelpItems(fileName: string, position: number): ts.SignatureHelpItems {
438-
return unwrapJSONCallResult(this.shim.getSignatureHelpItems(fileName, position));
437+
getSignatureHelpItems(fileName: string, position: number, options: ts.SignatureHelpItemsOptions | undefined): ts.SignatureHelpItems {
438+
return unwrapJSONCallResult(this.shim.getSignatureHelpItems(fileName, position, options));
439439
}
440440
getRenameInfo(fileName: string, position: number): ts.RenameInfo {
441441
return unwrapJSONCallResult(this.shim.getRenameInfo(fileName, position));

src/server/editorServices.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,7 @@ namespace ts.server {
471471

472472
this.hostConfiguration = {
473473
formatCodeOptions: getDefaultFormatCodeSettings(this.host),
474-
preferences: defaultPreferences,
474+
preferences: emptyOptions,
475475
hostInfo: "Unknown host",
476476
extraFileExtensions: []
477477
};

src/server/protocol.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1787,6 +1787,10 @@ namespace ts.server.protocol {
17871787
* Optional prefix to apply to possible completions.
17881788
*/
17891789
prefix?: string;
1790+
/**
1791+
* Character that was responsible for triggering completion.
1792+
* Should be `undefined` if a user manually requested completion.
1793+
*/
17901794
triggerCharacter?: CompletionsTriggerCharacter;
17911795
/**
17921796
* @deprecated Use UserPreferences.includeCompletionsForModuleExports
@@ -2048,10 +2052,17 @@ namespace ts.server.protocol {
20482052
argumentCount: number;
20492053
}
20502054

2055+
export type SignatureHelpTriggerCharacter = "," | "(" | "<";
2056+
20512057
/**
20522058
* Arguments of a signature help request.
20532059
*/
20542060
export interface SignatureHelpRequestArgs extends FileLocationRequestArgs {
2061+
/**
2062+
* Character that was responsible for triggering signature help.
2063+
* Should be `undefined` if a user manually requested completion.
2064+
*/
2065+
triggerCharacter?: SignatureHelpTriggerCharacter;
20552066
}
20562067

20572068
/**

src/server/scriptInfo.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,7 @@ namespace ts.server {
419419

420420
if (preferences) {
421421
if (!this.preferences) {
422-
this.preferences = defaultPreferences;
422+
this.preferences = emptyOptions;
423423
}
424424
this.preferences = { ...this.preferences, ...preferences };
425425
}

src/server/session.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1398,7 +1398,7 @@ namespace ts.server {
13981398
const { file, project } = this.getFileAndProject(args);
13991399
const scriptInfo = this.projectService.getScriptInfoForNormalizedPath(file)!;
14001400
const position = this.getPosition(args, scriptInfo);
1401-
const helpItems = project.getLanguageService().getSignatureHelpItems(file, position);
1401+
const helpItems = project.getLanguageService().getSignatureHelpItems(file, position, args);
14021402
if (!helpItems) {
14031403
return undefined;
14041404
}

src/services/services.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1425,7 +1425,7 @@ namespace ts {
14251425
return [...program.getOptionsDiagnostics(cancellationToken), ...program.getGlobalDiagnostics(cancellationToken)];
14261426
}
14271427

1428-
function getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions = defaultPreferences): CompletionInfo | undefined {
1428+
function getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions = emptyOptions): CompletionInfo | undefined {
14291429
// Convert from deprecated options names to new names
14301430
const fullPreferences: UserPreferences = {
14311431
...identity<UserPreferences>(options), // avoid excess property check
@@ -1443,7 +1443,7 @@ namespace ts {
14431443
options.triggerCharacter);
14441444
}
14451445

1446-
function getCompletionEntryDetails(fileName: string, position: number, name: string, formattingOptions: FormatCodeSettings | undefined, source: string | undefined, preferences: UserPreferences = defaultPreferences): CompletionEntryDetails | undefined {
1446+
function getCompletionEntryDetails(fileName: string, position: number, name: string, formattingOptions: FormatCodeSettings | undefined, source: string | undefined, preferences: UserPreferences = emptyOptions): CompletionEntryDetails | undefined {
14471447
synchronizeHostData();
14481448
return Completions.getCompletionEntryDetails(
14491449
program,
@@ -1769,12 +1769,12 @@ namespace ts {
17691769
/**
17701770
* This is a semantic operation.
17711771
*/
1772-
function getSignatureHelpItems(fileName: string, position: number): SignatureHelpItems | undefined {
1772+
function getSignatureHelpItems(fileName: string, position: number, { triggerCharacter }: SignatureHelpItemsOptions = emptyOptions): SignatureHelpItems | undefined {
17731773
synchronizeHostData();
17741774

17751775
const sourceFile = getValidSourceFile(fileName);
17761776

1777-
return SignatureHelp.getSignatureHelpItems(program, sourceFile, position, cancellationToken);
1777+
return SignatureHelp.getSignatureHelpItems(program, sourceFile, position, triggerCharacter, cancellationToken);
17781778
}
17791779

17801780
/// Syntactic features
@@ -1953,7 +1953,7 @@ namespace ts {
19531953
return [];
19541954
}
19551955

1956-
function getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray<number>, formatOptions: FormatCodeSettings, preferences: UserPreferences = defaultPreferences): ReadonlyArray<CodeFixAction> {
1956+
function getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray<number>, formatOptions: FormatCodeSettings, preferences: UserPreferences = emptyOptions): ReadonlyArray<CodeFixAction> {
19571957
synchronizeHostData();
19581958
const sourceFile = getValidSourceFile(fileName);
19591959
const span = createTextSpanFromBounds(start, end);
@@ -1965,7 +1965,7 @@ namespace ts {
19651965
});
19661966
}
19671967

1968-
function getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, preferences: UserPreferences = defaultPreferences): CombinedCodeActions {
1968+
function getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, preferences: UserPreferences = emptyOptions): CombinedCodeActions {
19691969
synchronizeHostData();
19701970
Debug.assert(scope.type === "file");
19711971
const sourceFile = getValidSourceFile(scope.fileName);
@@ -1974,7 +1974,7 @@ namespace ts {
19741974
return codefix.getAllFixes({ fixId, sourceFile, program, host, cancellationToken, formatContext, preferences });
19751975
}
19761976

1977-
function organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, preferences: UserPreferences = defaultPreferences): ReadonlyArray<FileTextChanges> {
1977+
function organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, preferences: UserPreferences = emptyOptions): ReadonlyArray<FileTextChanges> {
19781978
synchronizeHostData();
19791979
Debug.assert(scope.type === "file");
19801980
const sourceFile = getValidSourceFile(scope.fileName);
@@ -1983,7 +1983,7 @@ namespace ts {
19831983
return OrganizeImports.organizeImports(sourceFile, formatContext, host, program, preferences);
19841984
}
19851985

1986-
function getEditsForFileRename(oldFilePath: string, newFilePath: string, formatOptions: FormatCodeSettings, preferences: UserPreferences = defaultPreferences): ReadonlyArray<FileTextChanges> {
1986+
function getEditsForFileRename(oldFilePath: string, newFilePath: string, formatOptions: FormatCodeSettings, preferences: UserPreferences = emptyOptions): ReadonlyArray<FileTextChanges> {
19871987
return ts.getEditsForFileRename(getProgram()!, oldFilePath, newFilePath, host, formatting.getFormatContext(formatOptions), preferences);
19881988
}
19891989

@@ -2233,7 +2233,7 @@ namespace ts {
22332233
};
22342234
}
22352235

2236-
function getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences = defaultPreferences): ApplicableRefactorInfo[] {
2236+
function getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences = emptyOptions): ApplicableRefactorInfo[] {
22372237
synchronizeHostData();
22382238
const file = getValidSourceFile(fileName);
22392239
return refactor.getApplicableRefactors(getRefactorContext(file, positionOrRange, preferences));
@@ -2245,7 +2245,7 @@ namespace ts {
22452245
positionOrRange: number | TextRange,
22462246
refactorName: string,
22472247
actionName: string,
2248-
preferences: UserPreferences = defaultPreferences,
2248+
preferences: UserPreferences = emptyOptions,
22492249
): RefactorEditInfo | undefined {
22502250
synchronizeHostData();
22512251
const file = getValidSourceFile(fileName);

0 commit comments

Comments
 (0)