diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 8045f3a948b..32d5d6d4096 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1435,16 +1435,15 @@ Actual: ${stringify(fullActual)}`); } } - public verifyNoSignatureHelp(markers: ReadonlyArray) { + public verifyNoSignatureHelp(triggerCharacter: ts.SignatureHelpTriggerCharacter | undefined, markers: ReadonlyArray) { if (markers.length) { for (const marker of markers) { this.goToMarker(marker); - this.verifyNoSignatureHelp(ts.emptyArray); + this.verifyNoSignatureHelp(triggerCharacter, ts.emptyArray); } return; } - - const actual = this.getSignatureHelp(); + const actual = this.getSignatureHelp({ triggerCharacter }); if (actual) { this.raiseError(`Expected no signature help, but got "${stringify(actual)}"`); } @@ -1465,7 +1464,7 @@ Actual: ${stringify(fullActual)}`); } private verifySignatureHelpWorker(options: FourSlashInterface.VerifySignatureHelpOptions) { - const help = this.getSignatureHelp()!; + const help = this.getSignatureHelp({ triggerCharacter: options.triggerCharacter })!; const selectedItem = help.items[help.selectedItemIndex]; // Argument index may exceed number of parameters const currentParameter = selectedItem.parameters[help.argumentIndex] as ts.SignatureHelpParameter | undefined; @@ -1507,6 +1506,7 @@ Actual: ${stringify(fullActual)}`); const allKeys: ReadonlyArray = [ "marker", + "triggerCharacter", "overloadsCount", "docComment", "text", @@ -1733,7 +1733,7 @@ Actual: ${stringify(fullActual)}`); } public printCurrentParameterHelp() { - const help = this.languageService.getSignatureHelpItems(this.activeFile.fileName, this.currentCaretPosition); + const help = this.languageService.getSignatureHelpItems(this.activeFile.fileName, this.currentCaretPosition, /*options*/ undefined); Harness.IO.log(stringify(help)); } @@ -1774,12 +1774,14 @@ Actual: ${stringify(fullActual)}`); } public printCurrentSignatureHelp() { - const help = this.getSignatureHelp()!; + const help = this.getSignatureHelp(ts.emptyOptions)!; Harness.IO.log(stringify(help.items[help.selectedItemIndex])); } - private getSignatureHelp(): ts.SignatureHelpItems | undefined { - return this.languageService.getSignatureHelpItems(this.activeFile.fileName, this.currentCaretPosition); + private getSignatureHelp({ triggerCharacter }: FourSlashInterface.VerifySignatureHelpOptions): ts.SignatureHelpItems | undefined { + return this.languageService.getSignatureHelpItems(this.activeFile.fileName, this.currentCaretPosition, { + triggerCharacter + }); } public printCompletionListMembers(preferences: ts.UserPreferences | undefined) { @@ -1875,13 +1877,13 @@ Actual: ${stringify(fullActual)}`); offset++; if (highFidelity) { - if (ch === "(" || ch === ",") { + if (ch === "(" || ch === "," || ch === "<") { /* Signature help*/ - this.languageService.getSignatureHelpItems(this.activeFile.fileName, offset); + this.languageService.getSignatureHelpItems(this.activeFile.fileName, offset, { triggerCharacter: ch }); } else if (prevChar === " " && /A-Za-z_/.test(ch)) { /* Completions */ - this.languageService.getCompletionsAtPosition(this.activeFile.fileName, offset, ts.defaultPreferences); + this.languageService.getCompletionsAtPosition(this.activeFile.fileName, offset, ts.emptyOptions); } if (i % checkCadence === 0) { @@ -2514,7 +2516,7 @@ Actual: ${stringify(fullActual)}`); `Expected '${fixId}'. Available action ids: ${ts.mapDefined(this.getCodeFixes(this.activeFile.fileName), a => a.fixId)}`); ts.Debug.assertEqual(fixWithId!.fixAllDescription, fixAllDescription); - const { changes, commands } = this.languageService.getCombinedCodeFix({ type: "file", fileName: this.activeFile.fileName }, fixId, this.formatCodeSettings, ts.defaultPreferences); + const { changes, commands } = this.languageService.getCombinedCodeFix({ type: "file", fileName: this.activeFile.fileName }, fixId, this.formatCodeSettings, ts.emptyOptions); assert.deepEqual | undefined>(commands, expectedCommands); assert(changes.every(c => c.fileName === this.activeFile.fileName), "TODO: support testing codefixes that touch multiple files"); this.applyChanges(changes); @@ -2594,7 +2596,7 @@ Actual: ${stringify(fullActual)}`); * Rerieves a codefix satisfying the parameters, or undefined if no such codefix is found. * @param fileName Path to file where error should be retrieved from. */ - private getCodeFixes(fileName: string, errorCode?: number, preferences: ts.UserPreferences = ts.defaultPreferences): ts.CodeFixAction[] { + private getCodeFixes(fileName: string, errorCode?: number, preferences: ts.UserPreferences = ts.emptyOptions): ts.CodeFixAction[] { const diagnosticsForCodeFix = this.getDiagnostics(fileName, /*includeSuggestions*/ true).map(diagnostic => ({ start: diagnostic.start, length: diagnostic.length, @@ -3072,7 +3074,7 @@ Actual: ${stringify(fullActual)}`); this.raiseError(`Expected action description to be ${JSON.stringify(actionDescription)}, got: ${JSON.stringify(action.description)}`); } - const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, this.formatCodeSettings, range, refactorName, actionName, ts.defaultPreferences)!; + const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, this.formatCodeSettings, range, refactorName, actionName, ts.emptyOptions)!; for (const edit of editInfo.edits) { this.applyEdits(edit.fileName, edit.textChanges, /*isFormattingEdit*/ false); } @@ -3124,7 +3126,7 @@ Actual: ${stringify(fullActual)}`); const action = ts.first(refactor.actions); assert(action.name === "Move to a new file" && action.description === "Move to a new file"); - const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, this.formatCodeSettings, range, refactor.name, action.name, options.preferences || ts.defaultPreferences)!; + const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, this.formatCodeSettings, range, refactor.name, action.name, options.preferences || ts.emptyOptions)!; this.testNewFileContents(editInfo.edits, options.newFileContents); } @@ -3162,14 +3164,14 @@ Actual: ${stringify(fullActual)}`); formattingOptions = formattingOptions || this.formatCodeSettings; const markerPos = this.getMarkerByName(markerName).position; - const applicableRefactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, markerPos, ts.defaultPreferences); + const applicableRefactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, markerPos, ts.emptyOptions); const applicableRefactorToApply = ts.find(applicableRefactors, refactor => refactor.name === refactorNameToApply); if (!applicableRefactorToApply) { this.raiseError(`The expected refactor: ${refactorNameToApply} is not available at the marker location.`); } - const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, formattingOptions, markerPos, refactorNameToApply, actionName, ts.defaultPreferences)!; + const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, formattingOptions, markerPos, refactorNameToApply, actionName, ts.emptyOptions)!; for (const edit of editInfo.edits) { this.applyEdits(edit.fileName, edit.textChanges, /*isFormattingEdit*/ false); @@ -3358,11 +3360,11 @@ Actual: ${stringify(fullActual)}`); } public getEditsForFileRename(options: FourSlashInterface.GetEditsForFileRenameOptions): void { - const changes = this.languageService.getEditsForFileRename(options.oldPath, options.newPath, this.formatCodeSettings, ts.defaultPreferences); + const changes = this.languageService.getEditsForFileRename(options.oldPath, options.newPath, this.formatCodeSettings, ts.emptyOptions); this.testNewFileContents(changes, options.newFileContents); } - private getApplicableRefactors(positionOrRange: number | ts.TextRange, preferences = ts.defaultPreferences): ReadonlyArray { + private getApplicableRefactors(positionOrRange: number | ts.TextRange, preferences = ts.emptyOptions): ReadonlyArray { return this.languageService.getApplicableRefactors(this.activeFile.fileName, positionOrRange, preferences) || ts.emptyArray; } } @@ -4038,7 +4040,11 @@ namespace FourSlashInterface { } public noSignatureHelp(...markers: string[]): void { - this.state.verifyNoSignatureHelp(markers); + this.state.verifyNoSignatureHelp(/*triggerCharacter*/ undefined, markers); + } + + public noSignatureHelpForTriggerCharacter(triggerCharacter: ts.SignatureHelpTriggerCharacter, ...markers: string[]): void { + this.state.verifyNoSignatureHelp(triggerCharacter, markers); } public signatureHelp(...options: VerifySignatureHelpOptions[]): void { @@ -4765,6 +4771,7 @@ namespace FourSlashInterface { readonly isVariadic?: boolean; /** @default ts.emptyArray */ readonly tags?: ReadonlyArray; + readonly triggerCharacter?: ts.SignatureHelpTriggerCharacter; } export type ArrayOrSingle = T | ReadonlyArray; diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index bb476ee699a..885699215a5 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -434,8 +434,8 @@ namespace Harness.LanguageService { getBreakpointStatementAtPosition(fileName: string, position: number): ts.TextSpan { return unwrapJSONCallResult(this.shim.getBreakpointStatementAtPosition(fileName, position)); } - getSignatureHelpItems(fileName: string, position: number): ts.SignatureHelpItems { - return unwrapJSONCallResult(this.shim.getSignatureHelpItems(fileName, position)); + getSignatureHelpItems(fileName: string, position: number, options: ts.SignatureHelpItemsOptions | undefined): ts.SignatureHelpItems { + return unwrapJSONCallResult(this.shim.getSignatureHelpItems(fileName, position, options)); } getRenameInfo(fileName: string, position: number): ts.RenameInfo { return unwrapJSONCallResult(this.shim.getRenameInfo(fileName, position)); diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 77e5b2ed829..bba0ccee9b9 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -471,7 +471,7 @@ namespace ts.server { this.hostConfiguration = { formatCodeOptions: getDefaultFormatCodeSettings(this.host), - preferences: defaultPreferences, + preferences: emptyOptions, hostInfo: "Unknown host", extraFileExtensions: [] }; diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 3082dbae6aa..af8702f67ed 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -1787,6 +1787,10 @@ namespace ts.server.protocol { * Optional prefix to apply to possible completions. */ prefix?: string; + /** + * Character that was responsible for triggering completion. + * Should be `undefined` if a user manually requested completion. + */ triggerCharacter?: CompletionsTriggerCharacter; /** * @deprecated Use UserPreferences.includeCompletionsForModuleExports @@ -2048,10 +2052,17 @@ namespace ts.server.protocol { argumentCount: number; } + export type SignatureHelpTriggerCharacter = "," | "(" | "<"; + /** * Arguments of a signature help request. */ export interface SignatureHelpRequestArgs extends FileLocationRequestArgs { + /** + * Character that was responsible for triggering signature help. + * Should be `undefined` if a user manually requested completion. + */ + triggerCharacter?: SignatureHelpTriggerCharacter; } /** diff --git a/src/server/scriptInfo.ts b/src/server/scriptInfo.ts index e8f1d931ac2..37e07dbd7e8 100644 --- a/src/server/scriptInfo.ts +++ b/src/server/scriptInfo.ts @@ -419,7 +419,7 @@ namespace ts.server { if (preferences) { if (!this.preferences) { - this.preferences = defaultPreferences; + this.preferences = emptyOptions; } this.preferences = { ...this.preferences, ...preferences }; } diff --git a/src/server/session.ts b/src/server/session.ts index 88980367e84..c8e9b320b6d 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1398,7 +1398,7 @@ namespace ts.server { const { file, project } = this.getFileAndProject(args); const scriptInfo = this.projectService.getScriptInfoForNormalizedPath(file)!; const position = this.getPosition(args, scriptInfo); - const helpItems = project.getLanguageService().getSignatureHelpItems(file, position); + const helpItems = project.getLanguageService().getSignatureHelpItems(file, position, args); if (!helpItems) { return undefined; } diff --git a/src/services/services.ts b/src/services/services.ts index db0213ed88a..46f724e520e 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1425,7 +1425,7 @@ namespace ts { return [...program.getOptionsDiagnostics(cancellationToken), ...program.getGlobalDiagnostics(cancellationToken)]; } - function getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions = defaultPreferences): CompletionInfo | undefined { + function getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions = emptyOptions): CompletionInfo | undefined { // Convert from deprecated options names to new names const fullPreferences: UserPreferences = { ...identity(options), // avoid excess property check @@ -1443,7 +1443,7 @@ namespace ts { options.triggerCharacter); } - function getCompletionEntryDetails(fileName: string, position: number, name: string, formattingOptions: FormatCodeSettings | undefined, source: string | undefined, preferences: UserPreferences = defaultPreferences): CompletionEntryDetails | undefined { + function getCompletionEntryDetails(fileName: string, position: number, name: string, formattingOptions: FormatCodeSettings | undefined, source: string | undefined, preferences: UserPreferences = emptyOptions): CompletionEntryDetails | undefined { synchronizeHostData(); return Completions.getCompletionEntryDetails( program, @@ -1769,12 +1769,12 @@ namespace ts { /** * This is a semantic operation. */ - function getSignatureHelpItems(fileName: string, position: number): SignatureHelpItems | undefined { + function getSignatureHelpItems(fileName: string, position: number, { triggerCharacter }: SignatureHelpItemsOptions = emptyOptions): SignatureHelpItems | undefined { synchronizeHostData(); const sourceFile = getValidSourceFile(fileName); - return SignatureHelp.getSignatureHelpItems(program, sourceFile, position, cancellationToken); + return SignatureHelp.getSignatureHelpItems(program, sourceFile, position, triggerCharacter, cancellationToken); } /// Syntactic features @@ -1953,7 +1953,7 @@ namespace ts { return []; } - function getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings, preferences: UserPreferences = defaultPreferences): ReadonlyArray { + function getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings, preferences: UserPreferences = emptyOptions): ReadonlyArray { synchronizeHostData(); const sourceFile = getValidSourceFile(fileName); const span = createTextSpanFromBounds(start, end); @@ -1965,7 +1965,7 @@ namespace ts { }); } - function getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, preferences: UserPreferences = defaultPreferences): CombinedCodeActions { + function getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, preferences: UserPreferences = emptyOptions): CombinedCodeActions { synchronizeHostData(); Debug.assert(scope.type === "file"); const sourceFile = getValidSourceFile(scope.fileName); @@ -1974,7 +1974,7 @@ namespace ts { return codefix.getAllFixes({ fixId, sourceFile, program, host, cancellationToken, formatContext, preferences }); } - function organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, preferences: UserPreferences = defaultPreferences): ReadonlyArray { + function organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, preferences: UserPreferences = emptyOptions): ReadonlyArray { synchronizeHostData(); Debug.assert(scope.type === "file"); const sourceFile = getValidSourceFile(scope.fileName); @@ -1983,7 +1983,7 @@ namespace ts { return OrganizeImports.organizeImports(sourceFile, formatContext, host, program, preferences); } - function getEditsForFileRename(oldFilePath: string, newFilePath: string, formatOptions: FormatCodeSettings, preferences: UserPreferences = defaultPreferences): ReadonlyArray { + function getEditsForFileRename(oldFilePath: string, newFilePath: string, formatOptions: FormatCodeSettings, preferences: UserPreferences = emptyOptions): ReadonlyArray { return ts.getEditsForFileRename(getProgram()!, oldFilePath, newFilePath, host, formatting.getFormatContext(formatOptions), preferences); } @@ -2233,7 +2233,7 @@ namespace ts { }; } - function getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences = defaultPreferences): ApplicableRefactorInfo[] { + function getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences = emptyOptions): ApplicableRefactorInfo[] { synchronizeHostData(); const file = getValidSourceFile(fileName); return refactor.getApplicableRefactors(getRefactorContext(file, positionOrRange, preferences)); @@ -2245,7 +2245,7 @@ namespace ts { positionOrRange: number | TextRange, refactorName: string, actionName: string, - preferences: UserPreferences = defaultPreferences, + preferences: UserPreferences = emptyOptions, ): RefactorEditInfo | undefined { synchronizeHostData(); const file = getValidSourceFile(fileName); diff --git a/src/services/shims.ts b/src/services/shims.ts index 0ea27b4151f..0776f55c905 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -158,7 +158,7 @@ namespace ts { getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): string; getBreakpointStatementAtPosition(fileName: string, position: number): string; - getSignatureHelpItems(fileName: string, position: number): string; + getSignatureHelpItems(fileName: string, position: number, options: SignatureHelpItemsOptions | undefined): string; /** * Returns a JSON-encoded value of the type: @@ -769,10 +769,10 @@ namespace ts { /// SIGNATUREHELP - public getSignatureHelpItems(fileName: string, position: number): string { + public getSignatureHelpItems(fileName: string, position: number, options: SignatureHelpItemsOptions | undefined): string { return this.forwardJSONCall( `getSignatureHelpItems('${fileName}', ${position})`, - () => this.languageService.getSignatureHelpItems(fileName, position) + () => this.languageService.getSignatureHelpItems(fileName, position, options) ); } diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index ac13a2c7904..a183cdab1b4 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -19,7 +19,7 @@ namespace ts.SignatureHelp { argumentCount: number; } - export function getSignatureHelpItems(program: Program, sourceFile: SourceFile, position: number, cancellationToken: CancellationToken): SignatureHelpItems | undefined { + export function getSignatureHelpItems(program: Program, sourceFile: SourceFile, position: number, triggerCharacter: SignatureHelpTriggerCharacter | undefined, cancellationToken: CancellationToken): SignatureHelpItems | undefined { const typeChecker = program.getTypeChecker(); // Decide whether to show signature help @@ -29,6 +29,11 @@ namespace ts.SignatureHelp { return undefined; } + // In the middle of a string, don't provide signature help unless the user explicitly requested it. + if (triggerCharacter !== undefined && isInString(sourceFile, position, startingToken)) { + return undefined; + } + const argumentInfo = getContainingArgumentInfo(startingToken, position, sourceFile); if (!argumentInfo) return undefined; diff --git a/src/services/types.ts b/src/services/types.ts index 9ae51a4ea61..16476c795b0 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -242,7 +242,7 @@ namespace ts { readonly allowTextChangesInNewFiles?: boolean; } /* @internal */ - export const defaultPreferences: UserPreferences = {}; + export const emptyOptions = {}; // // Public services of a language service instance associated @@ -292,7 +292,7 @@ namespace ts { getBreakpointStatementAtPosition(fileName: string, position: number): TextSpan | undefined; - getSignatureHelpItems(fileName: string, position: number): SignatureHelpItems | undefined; + getSignatureHelpItems(fileName: string, position: number, options: SignatureHelpItemsOptions | undefined): SignatureHelpItems | undefined; getRenameInfo(fileName: string, position: number): RenameInfo; findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): RenameLocation[] | undefined; @@ -371,7 +371,10 @@ namespace ts { export type CompletionsTriggerCharacter = "." | '"' | "'" | "`" | "/" | "@" | "<"; export interface GetCompletionsAtPositionOptions extends UserPreferences { - /** If the editor is asking for completions because a certain character was typed, and not because the user explicitly requested them, this should be set. */ + /** + * If the editor is asking for completions because a certain character was typed + * (as opposed to when the user explicitly requested them) this should be set. + */ triggerCharacter?: CompletionsTriggerCharacter; /** @deprecated Use includeCompletionsForModuleExports */ includeExternalModuleExports?: boolean; @@ -379,6 +382,16 @@ namespace ts { includeInsertTextCompletions?: boolean; } + export type SignatureHelpTriggerCharacter = "," | "(" | "<"; + + export interface SignatureHelpItemsOptions { + /** + * If the editor is asking for signature help because a certain character was typed + * (as opposed to when the user explicitly requested them) this should be set. + */ + triggerCharacter?: SignatureHelpTriggerCharacter; + } + export interface ApplyCodeActionCommandResult { successMessage: string; } diff --git a/src/testRunner/unittests/cancellableLanguageServiceOperations.ts b/src/testRunner/unittests/cancellableLanguageServiceOperations.ts index a5947cc17e3..37f829d67a7 100644 --- a/src/testRunner/unittests/cancellableLanguageServiceOperations.ts +++ b/src/testRunner/unittests/cancellableLanguageServiceOperations.ts @@ -8,7 +8,7 @@ namespace ts { `; it("can cancel signature help mid-request", () => { verifyOperationCancelledAfter(file, 4, service => // Two calls are top-level in services, one is the root type, and the second should be for the parameter type - service.getSignatureHelpItems("file.ts", file.lastIndexOf("f"))!, + service.getSignatureHelpItems("file.ts", file.lastIndexOf("f"), emptyOptions)!, r => assert.exists(r.items[0]) ); }); diff --git a/src/testRunner/unittests/extractTestHelpers.ts b/src/testRunner/unittests/extractTestHelpers.ts index c36ac88e8e0..d812eb96f88 100644 --- a/src/testRunner/unittests/extractTestHelpers.ts +++ b/src/testRunner/unittests/extractTestHelpers.ts @@ -124,7 +124,7 @@ namespace ts { endPosition: selectionRange.end, host: notImplementedHost, formatContext: formatting.getFormatContext(testFormatOptions), - preferences: defaultPreferences, + preferences: emptyOptions, }; const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromRange(selectionRange)); assert.equal(rangeToExtract.errors, undefined, rangeToExtract.errors && "Range error: " + rangeToExtract.errors[0].messageText); @@ -188,7 +188,7 @@ namespace ts { endPosition: selectionRange.end, host: notImplementedHost, formatContext: formatting.getFormatContext(testFormatOptions), - preferences: defaultPreferences, + preferences: emptyOptions, }; const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromRange(selectionRange)); assert.isUndefined(rangeToExtract.errors, rangeToExtract.errors && "Range error: " + rangeToExtract.errors[0].messageText); diff --git a/src/testRunner/unittests/organizeImports.ts b/src/testRunner/unittests/organizeImports.ts index b889f97ef1c..7fe99ffc784 100644 --- a/src/testRunner/unittests/organizeImports.ts +++ b/src/testRunner/unittests/organizeImports.ts @@ -270,7 +270,7 @@ export const Other = 1; content: "function F() { }", }; const languageService = makeLanguageService(testFile); - const changes = languageService.organizeImports({ type: "file", fileName: testFile.path }, testFormatOptions, defaultPreferences); + const changes = languageService.organizeImports({ type: "file", fileName: testFile.path }, testFormatOptions, emptyOptions); assert.isEmpty(changes); }); @@ -741,7 +741,7 @@ export * from "lib"; function runBaseline(baselinePath: string, testFile: TestFSWithWatch.File, ...otherFiles: TestFSWithWatch.File[]) { const { path: testPath, content: testContent } = testFile; const languageService = makeLanguageService(testFile, ...otherFiles); - const changes = languageService.organizeImports({ type: "file", fileName: testPath }, testFormatOptions, defaultPreferences); + const changes = languageService.organizeImports({ type: "file", fileName: testPath }, testFormatOptions, emptyOptions); assert.equal(changes.length, 1); assert.equal(changes[0].fileName, testPath); diff --git a/src/testRunner/unittests/tsserverProjectSystem.ts b/src/testRunner/unittests/tsserverProjectSystem.ts index 04720fe42af..eea8530d516 100644 --- a/src/testRunner/unittests/tsserverProjectSystem.ts +++ b/src/testRunner/unittests/tsserverProjectSystem.ts @@ -1497,13 +1497,13 @@ namespace ts.projectSystem { service.checkNumberOfProjects({ externalProjects: 1 }); checkProjectActualFiles(service.externalProjects[0], [f1.path, f2.path, libFile.path]); - const completions1 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 2, defaultPreferences)!; + const completions1 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 2, emptyOptions)!; // should contain completions for string assert.isTrue(completions1.entries.some(e => e.name === "charAt"), "should contain 'charAt'"); assert.isFalse(completions1.entries.some(e => e.name === "toExponential"), "should not contain 'toExponential'"); service.closeClientFile(f2.path); - const completions2 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 2, defaultPreferences)!; + const completions2 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 2, emptyOptions)!; // should contain completions for string assert.isFalse(completions2.entries.some(e => e.name === "charAt"), "should not contain 'charAt'"); assert.isTrue(completions2.entries.some(e => e.name === "toExponential"), "should contain 'toExponential'"); @@ -1529,11 +1529,11 @@ namespace ts.projectSystem { service.checkNumberOfProjects({ externalProjects: 1 }); checkProjectActualFiles(service.externalProjects[0], [f1.path, f2.path, libFile.path]); - const completions1 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 0, defaultPreferences)!; + const completions1 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 0, emptyOptions)!; assert.isTrue(completions1.entries.some(e => e.name === "somelongname"), "should contain 'somelongname'"); service.closeClientFile(f2.path); - const completions2 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 0, defaultPreferences)!; + const completions2 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 0, emptyOptions)!; assert.isFalse(completions2.entries.some(e => e.name === "somelongname"), "should not contain 'somelongname'"); const sf2 = service.externalProjects[0].getLanguageService().getProgram()!.getSourceFile(f2.path)!; assert.equal(sf2.text, ""); @@ -2173,7 +2173,7 @@ namespace ts.projectSystem { // Check identifiers defined in HTML content are available in .ts file const project = configuredProjectAt(projectService, 0); - let completions = project.getLanguageService().getCompletionsAtPosition(file1.path, 1, defaultPreferences); + let completions = project.getLanguageService().getCompletionsAtPosition(file1.path, 1, emptyOptions); assert(completions && completions.entries[0].name === "hello", `expected entry hello to be in completion list`); // Close HTML file @@ -2187,7 +2187,7 @@ namespace ts.projectSystem { checkProjectActualFiles(configuredProjectAt(projectService, 0), [file1.path, file2.path, config.path]); // Check identifiers defined in HTML content are not available in .ts file - completions = project.getLanguageService().getCompletionsAtPosition(file1.path, 5, defaultPreferences); + completions = project.getLanguageService().getCompletionsAtPosition(file1.path, 5, emptyOptions); assert(completions && completions.entries[0].name !== "hello", `unexpected hello entry in completion list`); }); @@ -8613,7 +8613,7 @@ new C();` Debug.assert(!!project.resolveModuleNames); - const edits = project.getLanguageService().getEditsForFileRename("/old.ts", "/new.ts", testFormatOptions, defaultPreferences); + const edits = project.getLanguageService().getEditsForFileRename("/old.ts", "/new.ts", testFormatOptions, emptyOptions); assert.deepEqual>(edits, [{ fileName: "/user.ts", textChanges: [{ diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index a104e2fde0f..e287e323fe7 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -262,7 +262,8 @@ declare namespace FourSlashInterface { rangesAreRenameLocations(options?: Range[] | { findInStrings?: boolean, findInComments?: boolean, ranges?: Range[] }); findReferencesDefinitionDisplayPartsAtCaretAre(expected: ts.SymbolDisplayPart[]): void; noSignatureHelp(...markers: string[]): void; - signatureHelp(...options: VerifySignatureHelpOptions[]): void; + noSignatureHelpForTriggerCharacter(triggerCharacter: string, ...markers: string[]): void + signatureHelp(...options: VerifySignatureHelpOptions[], ): void; // Checks that there are no compile errors. noErrors(): void; numberOfErrorsInCurrentFile(expected: number): void; @@ -560,6 +561,7 @@ declare namespace FourSlashInterface { argumentCount?: number; isVariadic?: boolean; tags?: ReadonlyArray; + triggerCharacter?: string; } interface JSDocTagInfo { diff --git a/tests/cases/fourslash/signatureHelpFilteredTriggerCharacters01.ts b/tests/cases/fourslash/signatureHelpFilteredTriggerCharacters01.ts new file mode 100644 index 00000000000..2ff5d1c0c40 --- /dev/null +++ b/tests/cases/fourslash/signatureHelpFilteredTriggerCharacters01.ts @@ -0,0 +1,15 @@ +/// + +////function foo(x: T): T { +//// throw null; +////} +//// +////foo("/**/") + +goTo.marker(); +for (const triggerCharacter of ["<", "(", ","]) { + edit.insert(triggerCharacter); + verify.noSignatureHelpForTriggerCharacter(triggerCharacter); + edit.backspace(); +} +verify.signatureHelp({ triggerCharacter: undefined }); \ No newline at end of file