From a40fab8735a436a1b6768253b628875f05592d4f Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Wed, 28 Feb 2018 10:54:35 -0800 Subject: [PATCH 01/10] Support services settings --- src/harness/fourslash.ts | 7 +++-- src/harness/harnessLanguageService.ts | 4 +-- src/harness/unittests/session.ts | 2 +- .../unittests/tsserverProjectSystem.ts | 16 +++++----- src/server/editorServices.ts | 24 ++++++++------ src/server/protocol.ts | 6 ++++ src/server/scriptInfo.ts | 23 +++++++++----- src/server/session.ts | 31 ++++++++++++------- src/services/completions.ts | 31 +++++++++++-------- src/services/services.ts | 5 +-- src/services/shims.ts | 6 ++-- src/services/types.ts | 12 ++++++- .../reference/api/tsserverlibrary.d.ts | 21 ++++++++++--- tests/baselines/reference/api/typescript.d.ts | 5 ++- ...etionListInvalidMemberNames_escapeQuote.ts | 11 +++++-- tests/cases/fourslash/fourslash.ts | 4 +++ 16 files changed, 138 insertions(+), 70 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 88b1c32814e08..d0b9d4ad25750 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1221,7 +1221,7 @@ Actual: ${stringify(fullActual)}`); } private getCompletionListAtCaret(options?: FourSlashInterface.CompletionsAtOptions): ts.CompletionInfo { - return this.languageService.getCompletionsAtPosition(this.activeFile.fileName, this.currentCaretPosition, options); + return this.languageService.getCompletionsAtPosition(this.activeFile.fileName, this.currentCaretPosition, options, options && options.settings); } private getCompletionEntryDetails(entryName: string, source?: string): ts.CompletionEntryDetails { @@ -1818,7 +1818,7 @@ Actual: ${stringify(fullActual)}`); } else if (prevChar === " " && /A-Za-z_/.test(ch)) { /* Completions */ - this.languageService.getCompletionsAtPosition(this.activeFile.fileName, offset, { includeExternalModuleExports: false, includeInsertTextCompletions: false }); + this.languageService.getCompletionsAtPosition(this.activeFile.fileName, offset, ts.defaultCompletionOptions, ts.defaultServicesSettings); } if (i % checkCadence === 0) { @@ -2393,7 +2393,7 @@ Actual: ${stringify(fullActual)}`); public applyCodeActionFromCompletion(markerName: string, options: FourSlashInterface.VerifyCompletionActionOptions) { this.goToMarker(markerName); - const actualCompletion = this.getCompletionListAtCaret({ includeExternalModuleExports: true, includeInsertTextCompletions: false }).entries.find(e => + const actualCompletion = this.getCompletionListAtCaret({ ...ts.defaultCompletionOptions, includeExternalModuleExports: true }).entries.find(e => e.name === options.name && e.source === options.source); if (!actualCompletion.hasAction) { @@ -4618,6 +4618,7 @@ namespace FourSlashInterface { export type ExpectedCompletionEntry = string | { name: string, insertText?: string, replacementSpan?: FourSlash.Range }; export interface CompletionsAtOptions extends ts.GetCompletionsAtPositionOptions { isNewIdentifierLocation?: boolean; + settings?: ts.ServicesSettings; } export interface VerifyCompletionListContainsOptions extends ts.GetCompletionsAtPositionOptions { diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index c38ab1f3c6df3..ba3d092754e5c 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -417,8 +417,8 @@ namespace Harness.LanguageService { getEncodedSemanticClassifications(fileName: string, span: ts.TextSpan): ts.Classifications { return unwrapJSONCallResult(this.shim.getEncodedSemanticClassifications(fileName, span.start, span.length)); } - getCompletionsAtPosition(fileName: string, position: number, options: ts.GetCompletionsAtPositionOptions | undefined): ts.CompletionInfo { - return unwrapJSONCallResult(this.shim.getCompletionsAtPosition(fileName, position, options)); + getCompletionsAtPosition(fileName: string, position: number, options: ts.GetCompletionsAtPositionOptions | undefined, settings: ts.ServicesSettings): ts.CompletionInfo { + return unwrapJSONCallResult(this.shim.getCompletionsAtPosition(fileName, position, options, settings)); } getCompletionEntryDetails(fileName: string, position: number, entryName: string, options: ts.FormatCodeOptions | undefined, source: string | undefined): ts.CompletionEntryDetails { return unwrapJSONCallResult(this.shim.getCompletionEntryDetails(fileName, position, entryName, JSON.stringify(options), source)); diff --git a/src/harness/unittests/session.ts b/src/harness/unittests/session.ts index 765fc29ee49b7..ea34314af1906 100644 --- a/src/harness/unittests/session.ts +++ b/src/harness/unittests/session.ts @@ -145,7 +145,7 @@ namespace ts.server { session.onMessage(JSON.stringify(configureRequest)); - assert.equal(session.getProjectService().getFormatCodeOptions().indentStyle, IndentStyle.Block); + assert.equal(session.getProjectService().getFormatCodeOptions("" as NormalizedPath).indentStyle, IndentStyle.Block); const setOptionsRequest: protocol.SetCompilerOptionsForInferredProjectsRequest = { command: CommandNames.CompilerOptionsForInferredProjects, diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index e09dff2a6d5dd..ebd13e4616765 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -1321,13 +1321,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, { includeExternalModuleExports: false, includeInsertTextCompletions: false }); + const completions1 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 2, defaultCompletionOptions, defaultServicesSettings); // 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, { includeExternalModuleExports: false, includeInsertTextCompletions: false }); + const completions2 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 2, defaultCompletionOptions, defaultServicesSettings); // 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'"); @@ -1353,11 +1353,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, { includeExternalModuleExports: false, includeInsertTextCompletions: false }); + const completions1 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 0, defaultCompletionOptions, defaultServicesSettings); 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, { includeExternalModuleExports: false, includeInsertTextCompletions: false }); + const completions2 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 0, defaultCompletionOptions, defaultServicesSettings); 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, ""); @@ -1962,7 +1962,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, { includeExternalModuleExports: false, includeInsertTextCompletions: false }); + let completions = project.getLanguageService().getCompletionsAtPosition(file1.path, 1, defaultCompletionOptions, defaultServicesSettings); assert(completions && completions.entries[0].name === "hello", `expected entry hello to be in completion list`); // Close HTML file @@ -1976,7 +1976,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, { includeExternalModuleExports: false, includeInsertTextCompletions: false }); + completions = project.getLanguageService().getCompletionsAtPosition(file1.path, 5, defaultCompletionOptions, defaultServicesSettings); assert(completions && completions.entries[0].name !== "hello", `unexpected hello entry in completion list`); }); @@ -2629,7 +2629,7 @@ namespace ts.projectSystem { assert.equal(lastEvent.data.project, project, "project name"); assert.isFalse(lastEvent.data.languageServiceEnabled, "Language service state"); - const options = projectService.getFormatCodeOptions(); + const options = projectService.getFormatCodeOptions(f1.path as ts.server.NormalizedPath); const edits = project.getLanguageService().getFormattingEditsForDocument(f1.path, options); assert.deepEqual(edits, [{ span: createTextSpan(/*start*/ 7, /*length*/ 3), newText: " " }]); }); @@ -3525,7 +3525,7 @@ namespace ts.projectSystem { const projectService = createProjectService(host); projectService.openClientFile(f1.path); - const defaultSettings = projectService.getFormatCodeOptions(); + const defaultSettings = projectService.getFormatCodeOptions(f1.path as ts.server.NormalizedPath); // set global settings const newGlobalSettings1 = clone(defaultSettings); diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index c525d5a3bc821..9ad27511d4d63 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -200,6 +200,7 @@ namespace ts.server { export interface HostConfiguration { formatCodeOptions: FormatCodeSettings; + servicesOptions: ServicesSettings; hostInfo: string; extraFileExtensions?: JsFileExtensionInfo[]; } @@ -442,6 +443,7 @@ namespace ts.server { this.hostConfiguration = { formatCodeOptions: getDefaultFormatCodeSettings(this.host), + servicesOptions: defaultServicesSettings, hostInfo: "Unknown host", extraFileExtensions: [] }; @@ -703,15 +705,14 @@ namespace ts.server { return project.dirty && project.updateGraph(); } - getFormatCodeOptions(file?: NormalizedPath) { - let formatCodeSettings: FormatCodeSettings; - if (file) { - const info = this.getScriptInfoForNormalizedPath(file); - if (info) { - formatCodeSettings = info.getFormatCodeSettings(); - } - } - return formatCodeSettings || this.hostConfiguration.formatCodeOptions; + getFormatCodeOptions(file: NormalizedPath) { + const info = this.getScriptInfoForNormalizedPath(file); + return info && info.getFormatCodeSettings() || this.hostConfiguration.formatCodeOptions; + } + + getServicesSettings(file: NormalizedPath) { + const info = this.getScriptInfoForNormalizedPath(file); + return info && info.getServicesSettings() || this.hostConfiguration.servicesOptions; } private onSourceFileChanged(fileName: NormalizedPath, eventKind: FileWatcherEventKind) { @@ -1829,7 +1830,7 @@ namespace ts.server { if (args.file) { const info = this.getScriptInfoForNormalizedPath(toNormalizedPath(args.file)); if (info) { - info.setFormatOptions(convertFormatOptions(args.formatOptions)); + info.setSettings(convertFormatOptions(args.formatOptions), args.servicesOptions); this.logger.info(`Host configuration update for file ${args.file}`); } } @@ -1842,6 +1843,9 @@ namespace ts.server { mergeMapLikes(this.hostConfiguration.formatCodeOptions, convertFormatOptions(args.formatOptions)); this.logger.info("Format host information updated"); } + if (args.servicesOptions) { + mergeMapLikes(this.hostConfiguration.servicesOptions, args.servicesOptions); + } if (args.extraFileExtensions) { this.hostConfiguration.extraFileExtensions = args.extraFileExtensions; // We need to update the project structures again as it is possible that existing diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 76d954ebe23fa..28d9811045d78 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -1232,6 +1232,8 @@ namespace ts.server.protocol { */ formatOptions?: FormatCodeSettings; + servicesOptions?: ServicesSettings; + /** * The host's additional supported .js file extensions */ @@ -2588,6 +2590,10 @@ namespace ts.server.protocol { insertSpaceBeforeTypeAnnotation?: boolean; } + export interface ServicesSettings { + quote: '"' | "'"; + } + export interface CompilerOptions { allowJs?: boolean; allowSyntheticDefaultImports?: boolean; diff --git a/src/server/scriptInfo.ts b/src/server/scriptInfo.ts index 6fc4f241f6511..af19773b20c4b 100644 --- a/src/server/scriptInfo.ts +++ b/src/server/scriptInfo.ts @@ -204,7 +204,8 @@ namespace ts.server { * All projects that include this file */ readonly containingProjects: Project[] = []; - private formatCodeSettings: FormatCodeSettings; + private formatSettings: FormatCodeSettings | undefined; + private servicesSettings: ServicesSettings | undefined; /* @internal */ fileWatcher: FileWatcher; @@ -293,9 +294,8 @@ namespace ts.server { return this.realpath && this.realpath !== this.path ? this.realpath : undefined; } - getFormatCodeSettings() { - return this.formatCodeSettings; - } + getFormatCodeSettings(): FormatCodeSettings { return this.formatSettings; } + getServicesSettings(): ServicesSettings { return this.servicesSettings; } attachToProject(project: Project): boolean { const isNew = !this.isAttached(project); @@ -388,12 +388,19 @@ namespace ts.server { } } - setFormatOptions(formatSettings: FormatCodeSettings): void { + setSettings(formatSettings: FormatCodeSettings, servicesSettings: ServicesSettings): void { if (formatSettings) { - if (!this.formatCodeSettings) { - this.formatCodeSettings = getDefaultFormatCodeSettings(this.host); + if (!this.formatSettings) { + this.formatSettings = getDefaultFormatCodeSettings(this.host); + } + mergeMapLikes(this.formatSettings, formatSettings); + } + + if (servicesSettings) { + if (!this.servicesSettings) { + this.servicesSettings = clone(defaultServicesSettings); } - mergeMapLikes(this.formatCodeSettings, formatSettings); + mergeMapLikes(this.servicesSettings, servicesSettings); } } diff --git a/src/server/session.ts b/src/server/session.ts index bff19d7bc232f..6cd790c89be67 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1094,7 +1094,7 @@ namespace ts.server { private getIndentation(args: protocol.IndentationRequestArgs) { const { file, languageService } = this.getFileAndLanguageServiceForSyntacticOperation(args); const position = this.getPositionInFile(args, file); - const options = args.options ? convertFormatOptions(args.options) : this.projectService.getFormatCodeOptions(file); + const options = args.options ? convertFormatOptions(args.options) : this.formatSettings(file); const indentation = languageService.getIndentationAtPosition(file, position, options); return { position, indentation }; } @@ -1152,8 +1152,7 @@ namespace ts.server { const endPosition = scriptInfo.lineOffsetToPosition(args.endLine, args.endOffset); // TODO: avoid duplicate code (with formatonkey) - const edits = languageService.getFormattingEditsForRange(file, startPosition, endPosition, - this.projectService.getFormatCodeOptions(file)); + const edits = languageService.getFormattingEditsForRange(file, startPosition, endPosition, this.formatSettings(file)); if (!edits) { return undefined; } @@ -1163,19 +1162,19 @@ namespace ts.server { private getFormattingEditsForRangeFull(args: protocol.FormatRequestArgs) { const { file, languageService } = this.getFileAndLanguageServiceForSyntacticOperation(args); - const options = args.options ? convertFormatOptions(args.options) : this.projectService.getFormatCodeOptions(file); + const options = args.options ? convertFormatOptions(args.options) : this.formatSettings(file); return languageService.getFormattingEditsForRange(file, args.position, args.endPosition, options); } private getFormattingEditsForDocumentFull(args: protocol.FormatRequestArgs) { const { file, languageService } = this.getFileAndLanguageServiceForSyntacticOperation(args); - const options = args.options ? convertFormatOptions(args.options) : this.projectService.getFormatCodeOptions(file); + const options = args.options ? convertFormatOptions(args.options) : this.formatSettings(file); return languageService.getFormattingEditsForDocument(file, options); } private getFormattingEditsAfterKeystrokeFull(args: protocol.FormatOnKeyRequestArgs) { const { file, languageService } = this.getFileAndLanguageServiceForSyntacticOperation(args); - const options = args.options ? convertFormatOptions(args.options) : this.projectService.getFormatCodeOptions(file); + const options = args.options ? convertFormatOptions(args.options) : this.formatSettings(file); return languageService.getFormattingEditsAfterKeystroke(file, args.position, args.key, options); } @@ -1183,7 +1182,7 @@ namespace ts.server { const { file, languageService } = this.getFileAndLanguageServiceForSyntacticOperation(args); const scriptInfo = this.projectService.getScriptInfoForNormalizedPath(file); const position = scriptInfo.lineOffsetToPosition(args.line, args.offset); - const formatOptions = this.projectService.getFormatCodeOptions(file); + const formatOptions = this.formatSettings(file); const edits = languageService.getFormattingEditsAfterKeystroke(file, position, args.key, formatOptions); // Check whether we should auto-indent. This will be when @@ -1239,7 +1238,7 @@ namespace ts.server { const scriptInfo = this.projectService.getScriptInfoForNormalizedPath(file); const position = this.getPosition(args, scriptInfo); - const completions = project.getLanguageService().getCompletionsAtPosition(file, position, args); + const completions = project.getLanguageService().getCompletionsAtPosition(file, position, args, this.servicesSettings(file)); if (simplifiedResult) { return mapDefined(completions && completions.entries, entry => { if (completions.isMemberCompletion || startsWith(entry.name.toLowerCase(), prefix.toLowerCase())) { @@ -1571,7 +1570,7 @@ namespace ts.server { const result = project.getLanguageService().getEditsForRefactor( file, - this.projectService.getFormatCodeOptions(file), + this.formatSettings(file), position || textRange, args.refactor, args.action @@ -1600,7 +1599,7 @@ namespace ts.server { private organizeImports({ scope }: protocol.OrganizeImportsRequestArgs, simplifiedResult: boolean): ReadonlyArray | ReadonlyArray { Debug.assert(scope.type === "file"); const { file, project } = this.getFileAndProject(scope.args); - const formatOptions = this.projectService.getFormatCodeOptions(file); + const formatOptions = this.formatSettings(file); const changes = project.getLanguageService().organizeImports({ type: "file", fileName: file }, formatOptions); if (simplifiedResult) { return this.mapTextChangesToCodeEdits(project, changes); @@ -1618,7 +1617,7 @@ namespace ts.server { const scriptInfo = project.getScriptInfoForNormalizedPath(file); const { startPosition, endPosition } = this.getStartAndEndPosition(args, scriptInfo); - const formatOptions = this.projectService.getFormatCodeOptions(file); + const formatOptions = this.formatSettings(file); const codeActions = project.getLanguageService().getCodeFixesAtPosition(file, startPosition, endPosition, args.errorCodes, formatOptions); if (!codeActions) { @@ -1635,7 +1634,7 @@ namespace ts.server { private getCombinedCodeFix({ scope, fixId }: protocol.GetCombinedCodeFixRequestArgs, simplifiedResult: boolean): protocol.CombinedCodeActions | CombinedCodeActions { Debug.assert(scope.type === "file"); const { file, project } = this.getFileAndProject(scope.args); - const formatOptions = this.projectService.getFormatCodeOptions(file); + const formatOptions = this.formatSettings(file); const res = project.getLanguageService().getCombinedCodeFix({ type: "file", fileName: file }, fixId, formatOptions); if (simplifiedResult) { return { changes: this.mapTextChangesToCodeEdits(project, res.changes), commands: res.commands }; @@ -2151,6 +2150,14 @@ namespace ts.server { "Error processing request. " + (err).message + "\n" + (err).stack); } } + + private formatSettings(file: NormalizedPath): FormatCodeSettings { + return this.projectService.getFormatCodeOptions(file); + } + + private servicesSettings(file: NormalizedPath): ServicesSettings { + return this.projectService.getServicesSettings(file); + } } export interface HandlerResponse { diff --git a/src/services/completions.ts b/src/services/completions.ts index d07c2f494dab4..fb03e63256603 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -33,6 +33,7 @@ namespace ts.Completions { position: number, allSourceFiles: ReadonlyArray, options: GetCompletionsAtPositionOptions, + settings: ServicesSettings, ): CompletionInfo | undefined { if (isInReferenceComment(sourceFile, position)) { const entries = PathCompletions.getTripleSlashReferenceCompletion(sourceFile, position, compilerOptions, host); @@ -44,7 +45,7 @@ namespace ts.Completions { if (isInString(sourceFile, position, contextToken)) { return !contextToken || !isStringLiteralLike(contextToken) ? undefined - : convertStringLiteralCompletions(getStringLiteralCompletionEntries(sourceFile, contextToken, position, typeChecker, compilerOptions, host), sourceFile, typeChecker, log); + : convertStringLiteralCompletions(getStringLiteralCompletionEntries(sourceFile, contextToken, position, typeChecker, compilerOptions, host), sourceFile, typeChecker, log, settings); } if (contextToken && isBreakOrContinueStatement(contextToken.parent) @@ -59,7 +60,7 @@ namespace ts.Completions { switch (completionData.kind) { case CompletionDataKind.Data: - return completionInfoFromData(sourceFile, typeChecker, compilerOptions, log, completionData, options.includeInsertTextCompletions); + return completionInfoFromData(sourceFile, typeChecker, compilerOptions, log, completionData, options.includeInsertTextCompletions, settings); case CompletionDataKind.JsDocTagName: // If the current position is a jsDoc tag name, only tag names should be provided for completion return jsdocCompletionInfo(JsDoc.getJSDocTagNameCompletions()); @@ -73,7 +74,7 @@ namespace ts.Completions { } } - function convertStringLiteralCompletions(completion: StringLiteralCompletion | undefined, sourceFile: SourceFile, checker: TypeChecker, log: Log): CompletionInfo | undefined { + function convertStringLiteralCompletions(completion: StringLiteralCompletion | undefined, sourceFile: SourceFile, checker: TypeChecker, log: Log, settings: ServicesSettings): CompletionInfo | undefined { if (completion === undefined) { return undefined; } @@ -82,7 +83,7 @@ namespace ts.Completions { return convertPathCompletions(completion.paths); case StringLiteralCompletionKind.Properties: { const entries: CompletionEntry[] = []; - getCompletionEntriesFromSymbols(completion.symbols, entries, sourceFile, sourceFile, checker, ScriptTarget.ESNext, log, CompletionKind.String); // Target will not be used, so arbitrary + getCompletionEntriesFromSymbols(completion.symbols, entries, sourceFile, sourceFile, checker, ScriptTarget.ESNext, log, CompletionKind.String, settings); // Target will not be used, so arbitrary return { isGlobalCompletion: false, isMemberCompletion: true, isNewIdentifierLocation: true, entries }; } case StringLiteralCompletionKind.Types: { @@ -105,7 +106,7 @@ namespace ts.Completions { return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, entries }; } - function completionInfoFromData(sourceFile: SourceFile, typeChecker: TypeChecker, compilerOptions: CompilerOptions, log: Log, completionData: CompletionData, includeInsertTextCompletions: boolean): CompletionInfo { + function completionInfoFromData(sourceFile: SourceFile, typeChecker: TypeChecker, compilerOptions: CompilerOptions, log: Log, completionData: CompletionData, includeInsertTextCompletions: boolean, settings: ServicesSettings): CompletionInfo { const { symbols, completionKind, isInSnippetScope, isNewIdentifierLocation, location, propertyAccessToConvert, keywordFilters, symbolToOriginInfoMap, recommendedCompletion, isJsxInitializer } = completionData; if (sourceFile.languageVariant === LanguageVariant.JSX && location && location.parent && isJsxClosingElement(location.parent)) { @@ -127,7 +128,7 @@ namespace ts.Completions { const entries: CompletionEntry[] = []; if (isSourceFileJavaScript(sourceFile)) { - const uniqueNames = getCompletionEntriesFromSymbols(symbols, entries, location, sourceFile, typeChecker, compilerOptions.target, log, completionKind, includeInsertTextCompletions, propertyAccessToConvert, isJsxInitializer, recommendedCompletion, symbolToOriginInfoMap); + const uniqueNames = getCompletionEntriesFromSymbols(symbols, entries, location, sourceFile, typeChecker, compilerOptions.target, log, completionKind, settings, includeInsertTextCompletions, propertyAccessToConvert, isJsxInitializer, recommendedCompletion, symbolToOriginInfoMap); getJavaScriptCompletionEntries(sourceFile, location.pos, uniqueNames, compilerOptions.target, entries); } else { @@ -135,7 +136,7 @@ namespace ts.Completions { return undefined; } - getCompletionEntriesFromSymbols(symbols, entries, location, sourceFile, typeChecker, compilerOptions.target, log, completionKind, includeInsertTextCompletions, propertyAccessToConvert, isJsxInitializer, recommendedCompletion, symbolToOriginInfoMap); + getCompletionEntriesFromSymbols(symbols, entries, location, sourceFile, typeChecker, compilerOptions.target, log, completionKind, settings, includeInsertTextCompletions, propertyAccessToConvert, isJsxInitializer, recommendedCompletion, symbolToOriginInfoMap); } // TODO add filter for keyword based on type/value/namespace and also location @@ -197,6 +198,7 @@ namespace ts.Completions { propertyAccessToConvert: PropertyAccessExpression | undefined, isJsxInitializer: IsJsxInitializer, includeInsertTextCompletions: boolean, + settings: ServicesSettings, ): CompletionEntry | undefined { const info = getCompletionEntryDisplayNameForSymbol(symbol, target, origin, kind); if (!info) { @@ -208,10 +210,10 @@ namespace ts.Completions { let replacementSpan: TextSpan | undefined; if (includeInsertTextCompletions) { if (origin && origin.type === "this-type") { - insertText = needsConvertPropertyAccess ? `this[${quote(name)}]` : `this.${name}`; + insertText = needsConvertPropertyAccess ? `this[${quote(name, settings)}]` : `this.${name}`; } else if (needsConvertPropertyAccess) { - insertText = `[${quote(name)}]`; + insertText = `[${quote(name, settings)}]`; const dot = findChildOfKind(propertyAccessToConvert!, SyntaxKind.DotToken, sourceFile)!; // If the text after the '.' starts with this name, write over it. Else, add new text. const end = startsWith(name, propertyAccessToConvert!.name.text) ? propertyAccessToConvert!.name.end : dot.end; @@ -252,9 +254,11 @@ namespace ts.Completions { }; } - function quote(text: string): string { - // TODO: GH#20619 Use configured quote style - return JSON.stringify(text); + function quote(text: string, settings: ServicesSettings): string { + const quoted = JSON.stringify(text); + return settings.quote === "'" + ? `'${stripQuotes(quoted).replace("'", "\\'").replace('\\"', '"')}'` + : quoted; } function isRecommendedCompletionMatch(localSymbol: Symbol, recommendedCompletion: Symbol, checker: TypeChecker): boolean { @@ -279,6 +283,7 @@ namespace ts.Completions { target: ScriptTarget, log: Log, kind: CompletionKind, + settings: ServicesSettings, includeInsertTextCompletions?: boolean, propertyAccessToConvert?: PropertyAccessExpression | undefined, isJsxInitializer?: IsJsxInitializer, @@ -293,7 +298,7 @@ namespace ts.Completions { const uniques = createMap(); for (const symbol of symbols) { const origin = symbolToOriginInfoMap ? symbolToOriginInfoMap[getSymbolId(symbol)] : undefined; - const entry = createCompletionEntry(symbol, location, sourceFile, typeChecker, target, kind, origin, recommendedCompletion, propertyAccessToConvert, isJsxInitializer, includeInsertTextCompletions); + const entry = createCompletionEntry(symbol, location, sourceFile, typeChecker, target, kind, origin, recommendedCompletion, propertyAccessToConvert, isJsxInitializer, includeInsertTextCompletions, settings); if (!entry) { continue; } diff --git a/src/services/services.ts b/src/services/services.ts index a4da9b12eadd7..3419a4f5798f7 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1424,7 +1424,7 @@ namespace ts { return [...program.getOptionsDiagnostics(cancellationToken), ...program.getGlobalDiagnostics(cancellationToken)]; } - function getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions = { includeExternalModuleExports: false, includeInsertTextCompletions: false }): CompletionInfo { + function getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions = defaultCompletionOptions, settings: ServicesSettings = defaultServicesSettings): CompletionInfo { synchronizeHostData(); return Completions.getCompletionsAtPosition( host, @@ -1434,7 +1434,8 @@ namespace ts { getValidSourceFile(fileName), position, program.getSourceFiles(), - options); + options, + settings); } function getCompletionEntryDetails(fileName: string, position: number, name: string, formattingOptions?: FormatCodeSettings, source?: string): CompletionEntryDetails { diff --git a/src/services/shims.ts b/src/services/shims.ts index 5abf5da2c0719..120624d53d9ae 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -151,7 +151,7 @@ namespace ts { getEncodedSyntacticClassifications(fileName: string, start: number, length: number): string; getEncodedSemanticClassifications(fileName: string, start: number, length: number): string; - getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions | undefined): string; + getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions | undefined, settings: ServicesSettings | undefined): string; getCompletionEntryDetails(fileName: string, position: number, entryName: string, options: string/*Services.FormatCodeOptions*/ | undefined, source: string | undefined): string; getQuickInfoAtPosition(fileName: string, position: number): string; @@ -909,10 +909,10 @@ namespace ts { * to provide at the given source position and providing a member completion * list if requested. */ - public getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions | undefined) { + public getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions | undefined, settings: ServicesSettings | undefined) { return this.forwardJSONCall( `getCompletionsAtPosition('${fileName}', ${position}, ${options})`, - () => this.languageService.getCompletionsAtPosition(fileName, position, options) + () => this.languageService.getCompletionsAtPosition(fileName, position, options, settings) ); } diff --git a/src/services/types.ts b/src/services/types.ts index b67c2569f27f8..77e055351ab05 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -214,6 +214,14 @@ namespace ts { installPackage?(options: InstallPackageOptions): Promise; } + export interface ServicesSettings { + readonly quote: '"' | "'"; + } + /* @internal */ + export const defaultServicesSettings: ServicesSettings = { + quote: '"' + }; + // // Public services of a language service instance associated // with a language service host instance @@ -242,7 +250,7 @@ namespace ts { getEncodedSyntacticClassifications(fileName: string, span: TextSpan): Classifications; getEncodedSemanticClassifications(fileName: string, span: TextSpan): Classifications; - getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions | undefined): CompletionInfo; + getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions | undefined, settings: ServicesSettings | undefined): CompletionInfo; // "options" and "source" are optional only for backwards-compatibility getCompletionEntryDetails( fileName: string, @@ -333,6 +341,8 @@ namespace ts { includeExternalModuleExports: boolean; includeInsertTextCompletions: boolean; } + /* @internal */ + export const defaultCompletionOptions: GetCompletionsAtPositionOptions = { includeExternalModuleExports: false, includeInsertTextCompletions: false }; export interface ApplyCodeActionCommandResult { successMessage: string; diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 9f5ceb1f0cb5e..635e79dcfd2f9 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -4050,6 +4050,9 @@ declare namespace ts { isKnownTypesPackageName?(name: string): boolean; installPackage?(options: InstallPackageOptions): Promise; } + interface ServicesSettings { + readonly quote: '"' | "'"; + } interface LanguageService { cleanupSemanticCache(): void; getSyntacticDiagnostics(fileName: string): Diagnostic[]; @@ -4065,7 +4068,7 @@ declare namespace ts { getSemanticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[]; getEncodedSyntacticClassifications(fileName: string, span: TextSpan): Classifications; getEncodedSemanticClassifications(fileName: string, span: TextSpan): Classifications; - getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions | undefined): CompletionInfo; + getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions | undefined, settings: ServicesSettings | undefined): CompletionInfo; getCompletionEntryDetails(fileName: string, position: number, name: string, options: FormatCodeOptions | FormatCodeSettings | undefined, source: string | undefined): CompletionEntryDetails; getCompletionEntrySymbol(fileName: string, position: number, name: string, source: string | undefined): Symbol; getQuickInfoAtPosition(fileName: string, position: number): QuickInfo; @@ -5911,6 +5914,7 @@ declare namespace ts.server.protocol { * The format options to use during formatting and other code editing features. */ formatOptions?: FormatCodeSettings; + servicesOptions?: ServicesSettings; /** * The host's additional supported .js file extensions */ @@ -7041,6 +7045,9 @@ declare namespace ts.server.protocol { placeOpenBraceOnNewLineForControlBlocks?: boolean; insertSpaceBeforeTypeAnnotation?: boolean; } + interface ServicesSettings { + quote: '"' | "'"; + } interface CompilerOptions { allowJs?: boolean; allowSyntheticDefaultImports?: boolean; @@ -7303,6 +7310,8 @@ declare namespace ts.server { executeWithRequestId(requestId: number, f: () => T): T; executeCommand(request: protocol.Request): HandlerResponse; onMessage(message: string): void; + private formatSettings(file); + private servicesSettings(file); } interface HandlerResponse { response?: {}; @@ -7320,7 +7329,8 @@ declare namespace ts.server { * All projects that include this file */ readonly containingProjects: Project[]; - private formatCodeSettings; + private formatSettings; + private servicesSettings; private textStorage; constructor(host: ServerHost, fileName: NormalizedPath, scriptKind: ScriptKind, hasMixedContent: boolean, path: Path); isScriptOpen(): boolean; @@ -7329,13 +7339,14 @@ declare namespace ts.server { getSnapshot(): IScriptSnapshot; private ensureRealPath(); getFormatCodeSettings(): FormatCodeSettings; + getServicesSettings(): ServicesSettings; attachToProject(project: Project): boolean; isAttached(project: Project): boolean; detachFromProject(project: Project): void; detachAllProjects(): void; getDefaultProject(): Project; registerFileUpdate(): void; - setFormatOptions(formatSettings: FormatCodeSettings): void; + setSettings(formatSettings: FormatCodeSettings, servicesSettings: ServicesSettings): void; getLatestVersion(): string; saveTo(fileName: string): void; reloadFromFile(tempFileName?: NormalizedPath): void; @@ -7713,6 +7724,7 @@ declare namespace ts.server { function convertScriptKindName(scriptKindName: protocol.ScriptKindName): ScriptKind.Unknown | ScriptKind.JS | ScriptKind.JSX | ScriptKind.TS | ScriptKind.TSX; interface HostConfiguration { formatCodeOptions: FormatCodeSettings; + servicesOptions: ServicesSettings; hostInfo: string; extraFileExtensions?: JsFileExtensionInfo[]; } @@ -7822,7 +7834,8 @@ declare namespace ts.server { */ private ensureProjectStructuresUptoDate(); private updateProjectIfDirty(project); - getFormatCodeOptions(file?: NormalizedPath): FormatCodeSettings; + getFormatCodeOptions(file: NormalizedPath): FormatCodeSettings; + getServicesSettings(file: NormalizedPath): ServicesSettings; private onSourceFileChanged(fileName, eventKind); private handleDeletedFile(info); private onConfigChangedForConfiguredProject(project, eventKind); diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index e5fb0b1e1692c..ab09b1afce88b 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -4302,6 +4302,9 @@ declare namespace ts { isKnownTypesPackageName?(name: string): boolean; installPackage?(options: InstallPackageOptions): Promise; } + interface ServicesSettings { + readonly quote: '"' | "'"; + } interface LanguageService { cleanupSemanticCache(): void; getSyntacticDiagnostics(fileName: string): Diagnostic[]; @@ -4317,7 +4320,7 @@ declare namespace ts { getSemanticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[]; getEncodedSyntacticClassifications(fileName: string, span: TextSpan): Classifications; getEncodedSemanticClassifications(fileName: string, span: TextSpan): Classifications; - getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions | undefined): CompletionInfo; + getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions | undefined, settings: ServicesSettings | undefined): CompletionInfo; getCompletionEntryDetails(fileName: string, position: number, name: string, options: FormatCodeOptions | FormatCodeSettings | undefined, source: string | undefined): CompletionEntryDetails; getCompletionEntrySymbol(fileName: string, position: number, name: string, source: string | undefined): Symbol; getQuickInfoAtPosition(fileName: string, position: number): QuickInfo; diff --git a/tests/cases/fourslash/completionListInvalidMemberNames_escapeQuote.ts b/tests/cases/fourslash/completionListInvalidMemberNames_escapeQuote.ts index 8d6dde442a1be..9af041b2f7db5 100644 --- a/tests/cases/fourslash/completionListInvalidMemberNames_escapeQuote.ts +++ b/tests/cases/fourslash/completionListInvalidMemberNames_escapeQuote.ts @@ -1,7 +1,14 @@ /// -////declare const x: { '"': 0 }; +////declare const x: { "\"'": 0 }; ////x[|./**/|]; const replacementSpan = test.ranges()[0]; -verify.completionsAt("", [{ name: '"', insertText: '["\\""]', replacementSpan }], { includeInsertTextCompletions: true }); +verify.completionsAt("", [{ name: `"'`, insertText: `["\\"'"]`, replacementSpan }], { includeInsertTextCompletions: true }); + +verify.completionsAt("", [{ name: `"'`, insertText: `['"\\'']`, replacementSpan }], { + includeInsertTextCompletions: true, + settings: { + quote: "'", + }, +}); diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 9680eb822a73f..af3e08431935a 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -199,6 +199,7 @@ declare namespace FourSlashInterface { completionsAt(markerName: string, completions: ReadonlyArray, options?: { isNewIdentifierLocation?: boolean; includeInsertTextCompletions?: boolean; + settings?: ServicesSettings; }): void; completionsAndDetailsAt( markerName: string, @@ -527,6 +528,9 @@ declare namespace FourSlashInterface { category: string; code: number; } + interface ServicesSettings { + quote?: '"' | "'"; + } } declare function verifyOperationIsCancelled(f: any): void; declare var test: FourSlashInterface.test_; From 590faf04be85dd9d68c5b703416905d45279033a Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 5 Mar 2018 13:22:20 -0800 Subject: [PATCH 02/10] Code review --- src/harness/fourslash.ts | 20 +++---- src/harness/harnessLanguageService.ts | 4 +- .../unittests/tsserverProjectSystem.ts | 16 ++--- src/server/client.ts | 5 +- src/server/editorServices.ts | 14 ++--- src/server/protocol.ts | 26 ++++++--- src/server/scriptInfo.ts | 14 ++--- src/server/session.ts | 34 ++++++----- src/services/codeFixProvider.ts | 1 + src/services/completions.ts | 49 ++++++++-------- src/services/services.ts | 11 ++-- src/services/shims.ts | 8 +-- src/services/types.ts | 24 ++++---- .../reference/api/tsserverlibrary.d.ts | 58 +++++++++++-------- tests/baselines/reference/api/typescript.d.ts | 18 +++--- ...etionListInvalidMemberNames_escapeQuote.ts | 4 +- tests/cases/fourslash/fourslash.ts | 18 +++--- 17 files changed, 168 insertions(+), 156 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index d0b9d4ad25750..cd8132760ceb9 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -365,7 +365,6 @@ namespace FourSlash { function memoWrap(ls: ts.LanguageService, target: TestState): ts.LanguageService { const cacheableMembers: (keyof typeof ls)[] = [ - "getCompletionsAtPosition", "getCompletionEntryDetails", "getCompletionEntrySymbol", "getQuickInfoAtPosition", @@ -1221,7 +1220,7 @@ Actual: ${stringify(fullActual)}`); } private getCompletionListAtCaret(options?: FourSlashInterface.CompletionsAtOptions): ts.CompletionInfo { - return this.languageService.getCompletionsAtPosition(this.activeFile.fileName, this.currentCaretPosition, options, options && options.settings); + return this.languageService.getCompletionsAtPosition(this.activeFile.fileName, this.currentCaretPosition, options); } private getCompletionEntryDetails(entryName: string, source?: string): ts.CompletionEntryDetails { @@ -1719,7 +1718,7 @@ Actual: ${stringify(fullActual)}`); Harness.IO.log(stringify(sigHelp)); } - public printCompletionListMembers(options: ts.GetCompletionsAtPositionOptions | undefined) { + public printCompletionListMembers(options: ts.Options | undefined) { const completions = this.getCompletionListAtCaret(options); this.printMembersOrCompletions(completions); } @@ -1818,7 +1817,7 @@ Actual: ${stringify(fullActual)}`); } else if (prevChar === " " && /A-Za-z_/.test(ch)) { /* Completions */ - this.languageService.getCompletionsAtPosition(this.activeFile.fileName, offset, ts.defaultCompletionOptions, ts.defaultServicesSettings); + this.languageService.getCompletionsAtPosition(this.activeFile.fileName, offset, ts.defaultOptions); } if (i % checkCadence === 0) { @@ -2393,7 +2392,7 @@ Actual: ${stringify(fullActual)}`); public applyCodeActionFromCompletion(markerName: string, options: FourSlashInterface.VerifyCompletionActionOptions) { this.goToMarker(markerName); - const actualCompletion = this.getCompletionListAtCaret({ ...ts.defaultCompletionOptions, includeExternalModuleExports: true }).entries.find(e => + const actualCompletion = this.getCompletionListAtCaret({ ...ts.defaultOptions, includeExternalModuleExports: true }).entries.find(e => e.name === options.name && e.source === options.source); if (!actualCompletion.hasAction) { @@ -2445,7 +2444,7 @@ Actual: ${stringify(fullActual)}`); const { fixId, newFileContent } = options; const fixIds = ts.mapDefined(this.getCodeFixes(this.activeFile.fileName), a => a.fixId); ts.Debug.assert(ts.contains(fixIds, fixId), "No available code fix has that group id.", () => `Expected '${fixId}'. Available action ids: ${fixIds}`); - const { changes, commands } = this.languageService.getCombinedCodeFix({ type: "file", fileName: this.activeFile.fileName }, fixId, this.formatCodeSettings); + const { changes, commands } = this.languageService.getCombinedCodeFix({ type: "file", fileName: this.activeFile.fileName }, fixId, this.formatCodeSettings, ts.defaultOptions); assert.deepEqual(commands, options.commands); assert(changes.every(c => c.fileName === this.activeFile.fileName), "TODO: support testing codefixes that touch multiple files"); this.applyChanges(changes); @@ -2525,7 +2524,7 @@ Actual: ${stringify(fullActual)}`); return; } - return this.languageService.getCodeFixesAtPosition(fileName, diagnostic.start, diagnostic.start + diagnostic.length, [diagnostic.code], this.formatCodeSettings); + return this.languageService.getCodeFixesAtPosition(fileName, diagnostic.start, diagnostic.start + diagnostic.length, [diagnostic.code], this.formatCodeSettings, ts.defaultOptions); }); } @@ -4419,7 +4418,7 @@ namespace FourSlashInterface { this.state.printCurrentSignatureHelp(); } - public printCompletionListMembers(options: ts.GetCompletionsAtPositionOptions | undefined) { + public printCompletionListMembers(options: ts.Options | undefined) { this.state.printCompletionListMembers(options); } @@ -4616,12 +4615,11 @@ namespace FourSlashInterface { } export type ExpectedCompletionEntry = string | { name: string, insertText?: string, replacementSpan?: FourSlash.Range }; - export interface CompletionsAtOptions extends ts.GetCompletionsAtPositionOptions { + export interface CompletionsAtOptions extends Partial { isNewIdentifierLocation?: boolean; - settings?: ts.ServicesSettings; } - export interface VerifyCompletionListContainsOptions extends ts.GetCompletionsAtPositionOptions { + export interface VerifyCompletionListContainsOptions extends ts.Options { sourceDisplay: string; isRecommended?: true; insertText?: string; diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index ba3d092754e5c..f94a07622d37d 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -417,8 +417,8 @@ namespace Harness.LanguageService { getEncodedSemanticClassifications(fileName: string, span: ts.TextSpan): ts.Classifications { return unwrapJSONCallResult(this.shim.getEncodedSemanticClassifications(fileName, span.start, span.length)); } - getCompletionsAtPosition(fileName: string, position: number, options: ts.GetCompletionsAtPositionOptions | undefined, settings: ts.ServicesSettings): ts.CompletionInfo { - return unwrapJSONCallResult(this.shim.getCompletionsAtPosition(fileName, position, options, settings)); + getCompletionsAtPosition(fileName: string, position: number, options: ts.Options | undefined): ts.CompletionInfo { + return unwrapJSONCallResult(this.shim.getCompletionsAtPosition(fileName, position, options)); } getCompletionEntryDetails(fileName: string, position: number, entryName: string, options: ts.FormatCodeOptions | undefined, source: string | undefined): ts.CompletionEntryDetails { return unwrapJSONCallResult(this.shim.getCompletionEntryDetails(fileName, position, entryName, JSON.stringify(options), source)); diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index ebd13e4616765..c4d923f739e9f 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -1321,13 +1321,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, defaultCompletionOptions, defaultServicesSettings); + const completions1 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 2, defaultOptions); // 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, defaultCompletionOptions, defaultServicesSettings); + const completions2 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 2, defaultOptions); // 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'"); @@ -1353,11 +1353,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, defaultCompletionOptions, defaultServicesSettings); + const completions1 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 0, defaultOptions); 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, defaultCompletionOptions, defaultServicesSettings); + const completions2 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 0, defaultOptions); 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, ""); @@ -1962,7 +1962,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, defaultCompletionOptions, defaultServicesSettings); + let completions = project.getLanguageService().getCompletionsAtPosition(file1.path, 1, defaultOptions); assert(completions && completions.entries[0].name === "hello", `expected entry hello to be in completion list`); // Close HTML file @@ -1976,7 +1976,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, defaultCompletionOptions, defaultServicesSettings); + completions = project.getLanguageService().getCompletionsAtPosition(file1.path, 5, defaultOptions); assert(completions && completions.entries[0].name !== "hello", `unexpected hello entry in completion list`); }); @@ -2629,7 +2629,7 @@ namespace ts.projectSystem { assert.equal(lastEvent.data.project, project, "project name"); assert.isFalse(lastEvent.data.languageServiceEnabled, "Language service state"); - const options = projectService.getFormatCodeOptions(f1.path as ts.server.NormalizedPath); + const options = projectService.getFormatCodeOptions(f1.path as server.NormalizedPath); const edits = project.getLanguageService().getFormattingEditsForDocument(f1.path, options); assert.deepEqual(edits, [{ span: createTextSpan(/*start*/ 7, /*length*/ 3), newText: " " }]); }); @@ -3525,7 +3525,7 @@ namespace ts.projectSystem { const projectService = createProjectService(host); projectService.openClientFile(f1.path); - const defaultSettings = projectService.getFormatCodeOptions(f1.path as ts.server.NormalizedPath); + const defaultSettings = projectService.getFormatCodeOptions(f1.path as server.NormalizedPath); // set global settings const newGlobalSettings1 = clone(defaultSettings); diff --git a/src/server/client.ts b/src/server/client.ts index cee65c0e5a485..8078870e31139 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -169,8 +169,9 @@ namespace ts.server { }; } - getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions | undefined): CompletionInfo { - const args: protocol.CompletionsRequestArgs = { ...this.createFileLocationRequestArgs(fileName, position), ...options }; + getCompletionsAtPosition(fileName: string, position: number, _settings: Options | undefined): CompletionInfo { + // Not passing along 'settings' because server should already have those from the 'configure' command + const args: protocol.CompletionsRequestArgs = this.createFileLocationRequestArgs(fileName, position); const request = this.processRequest(CommandNames.Completions, args); const response = this.processResponse(request); diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 9ad27511d4d63..e3cce13ae4bfb 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -200,7 +200,7 @@ namespace ts.server { export interface HostConfiguration { formatCodeOptions: FormatCodeSettings; - servicesOptions: ServicesSettings; + options: Options; hostInfo: string; extraFileExtensions?: JsFileExtensionInfo[]; } @@ -443,7 +443,7 @@ namespace ts.server { this.hostConfiguration = { formatCodeOptions: getDefaultFormatCodeSettings(this.host), - servicesOptions: defaultServicesSettings, + options: defaultOptions, hostInfo: "Unknown host", extraFileExtensions: [] }; @@ -710,9 +710,9 @@ namespace ts.server { return info && info.getFormatCodeSettings() || this.hostConfiguration.formatCodeOptions; } - getServicesSettings(file: NormalizedPath) { + getOptions(file: NormalizedPath): Options { const info = this.getScriptInfoForNormalizedPath(file); - return info && info.getServicesSettings() || this.hostConfiguration.servicesOptions; + return info && info.getOptions() || this.hostConfiguration.options; } private onSourceFileChanged(fileName: NormalizedPath, eventKind: FileWatcherEventKind) { @@ -1830,7 +1830,7 @@ namespace ts.server { if (args.file) { const info = this.getScriptInfoForNormalizedPath(toNormalizedPath(args.file)); if (info) { - info.setSettings(convertFormatOptions(args.formatOptions), args.servicesOptions); + info.setOptions(convertFormatOptions(args.formatOptions), args.options); this.logger.info(`Host configuration update for file ${args.file}`); } } @@ -1843,8 +1843,8 @@ namespace ts.server { mergeMapLikes(this.hostConfiguration.formatCodeOptions, convertFormatOptions(args.formatOptions)); this.logger.info("Format host information updated"); } - if (args.servicesOptions) { - mergeMapLikes(this.hostConfiguration.servicesOptions, args.servicesOptions); + if (args.options) { + mergeMapLikes(this.hostConfiguration.options, args.options); } if (args.extraFileExtensions) { this.hostConfiguration.extraFileExtensions = args.extraFileExtensions; diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 28d9811045d78..58aa25ed56daa 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -1232,7 +1232,7 @@ namespace ts.server.protocol { */ formatOptions?: FormatCodeSettings; - servicesOptions?: ServicesSettings; + options?: Options; /** * The host's additional supported .js file extensions @@ -1709,15 +1709,13 @@ namespace ts.server.protocol { */ prefix?: string; /** - * If enabled, TypeScript will search through all external modules' exports and add them to the completions list. - * This affects lone identifier completions but not completions on the right hand side of `obj.`. + * @deprecated Use Options */ - includeExternalModuleExports: boolean; + includeExternalModuleExports?: boolean; /** - * If enabled, the completion list will include completions with invalid identifier names. - * For those entries, The `insertText` and `replacementSpan` properties will be set to change from `.x` property access to `["x"]`. + * @deprecated Use Options */ - includeInsertTextCompletions: boolean; + includeInsertTextCompletions?: boolean; } /** @@ -2590,8 +2588,18 @@ namespace ts.server.protocol { insertSpaceBeforeTypeAnnotation?: boolean; } - export interface ServicesSettings { - quote: '"' | "'"; + export interface Options { + quote: "double" | "single"; + /** + * If enabled, TypeScript will search through all external modules' exports and add them to the completions list. + * This affects lone identifier completions but not completions on the right hand side of `obj.`. + */ + includeExternalModuleExports: boolean; + /** + * If enabled, the completion list will include completions with invalid identifier names. + * For those entries, The `insertText` and `replacementSpan` properties will be set to change from `.x` property access to `["x"]`. + */ + includeInsertTextCompletions: boolean; } export interface CompilerOptions { diff --git a/src/server/scriptInfo.ts b/src/server/scriptInfo.ts index af19773b20c4b..b73708861357e 100644 --- a/src/server/scriptInfo.ts +++ b/src/server/scriptInfo.ts @@ -205,7 +205,7 @@ namespace ts.server { */ readonly containingProjects: Project[] = []; private formatSettings: FormatCodeSettings | undefined; - private servicesSettings: ServicesSettings | undefined; + private options: Options | undefined; /* @internal */ fileWatcher: FileWatcher; @@ -295,7 +295,7 @@ namespace ts.server { } getFormatCodeSettings(): FormatCodeSettings { return this.formatSettings; } - getServicesSettings(): ServicesSettings { return this.servicesSettings; } + getOptions(): Options { return this.options; } attachToProject(project: Project): boolean { const isNew = !this.isAttached(project); @@ -388,7 +388,7 @@ namespace ts.server { } } - setSettings(formatSettings: FormatCodeSettings, servicesSettings: ServicesSettings): void { + setOptions(formatSettings: FormatCodeSettings, options: Options): void { if (formatSettings) { if (!this.formatSettings) { this.formatSettings = getDefaultFormatCodeSettings(this.host); @@ -396,11 +396,11 @@ namespace ts.server { mergeMapLikes(this.formatSettings, formatSettings); } - if (servicesSettings) { - if (!this.servicesSettings) { - this.servicesSettings = clone(defaultServicesSettings); + if (options) { + if (!this.options) { + this.options = clone(defaultOptions); } - mergeMapLikes(this.servicesSettings, servicesSettings); + mergeMapLikes(this.options, options); } } diff --git a/src/server/session.ts b/src/server/session.ts index 6cd790c89be67..0b20f829f125d 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1094,7 +1094,7 @@ namespace ts.server { private getIndentation(args: protocol.IndentationRequestArgs) { const { file, languageService } = this.getFileAndLanguageServiceForSyntacticOperation(args); const position = this.getPositionInFile(args, file); - const options = args.options ? convertFormatOptions(args.options) : this.formatSettings(file); + const options = args.options ? convertFormatOptions(args.options) : this.getFormatOptions(file); const indentation = languageService.getIndentationAtPosition(file, position, options); return { position, indentation }; } @@ -1152,7 +1152,7 @@ namespace ts.server { const endPosition = scriptInfo.lineOffsetToPosition(args.endLine, args.endOffset); // TODO: avoid duplicate code (with formatonkey) - const edits = languageService.getFormattingEditsForRange(file, startPosition, endPosition, this.formatSettings(file)); + const edits = languageService.getFormattingEditsForRange(file, startPosition, endPosition, this.getFormatOptions(file)); if (!edits) { return undefined; } @@ -1162,19 +1162,19 @@ namespace ts.server { private getFormattingEditsForRangeFull(args: protocol.FormatRequestArgs) { const { file, languageService } = this.getFileAndLanguageServiceForSyntacticOperation(args); - const options = args.options ? convertFormatOptions(args.options) : this.formatSettings(file); + const options = args.options ? convertFormatOptions(args.options) : this.getFormatOptions(file); return languageService.getFormattingEditsForRange(file, args.position, args.endPosition, options); } private getFormattingEditsForDocumentFull(args: protocol.FormatRequestArgs) { const { file, languageService } = this.getFileAndLanguageServiceForSyntacticOperation(args); - const options = args.options ? convertFormatOptions(args.options) : this.formatSettings(file); + const options = args.options ? convertFormatOptions(args.options) : this.getFormatOptions(file); return languageService.getFormattingEditsForDocument(file, options); } private getFormattingEditsAfterKeystrokeFull(args: protocol.FormatOnKeyRequestArgs) { const { file, languageService } = this.getFileAndLanguageServiceForSyntacticOperation(args); - const options = args.options ? convertFormatOptions(args.options) : this.formatSettings(file); + const options = args.options ? convertFormatOptions(args.options) : this.getFormatOptions(file); return languageService.getFormattingEditsAfterKeystroke(file, args.position, args.key, options); } @@ -1182,7 +1182,7 @@ namespace ts.server { const { file, languageService } = this.getFileAndLanguageServiceForSyntacticOperation(args); const scriptInfo = this.projectService.getScriptInfoForNormalizedPath(file); const position = scriptInfo.lineOffsetToPosition(args.line, args.offset); - const formatOptions = this.formatSettings(file); + const formatOptions = this.getFormatOptions(file); const edits = languageService.getFormattingEditsAfterKeystroke(file, position, args.key, formatOptions); // Check whether we should auto-indent. This will be when @@ -1238,7 +1238,11 @@ namespace ts.server { const scriptInfo = this.projectService.getScriptInfoForNormalizedPath(file); const position = this.getPosition(args, scriptInfo); - const completions = project.getLanguageService().getCompletionsAtPosition(file, position, args, this.servicesSettings(file)); + const completions = project.getLanguageService().getCompletionsAtPosition(file, position, { + ...this.getServicesOptions(file), + includeExternalModuleExports: args.includeExternalModuleExports, + includeInsertTextCompletions: args.includeInsertTextCompletions + }); if (simplifiedResult) { return mapDefined(completions && completions.entries, entry => { if (completions.isMemberCompletion || startsWith(entry.name.toLowerCase(), prefix.toLowerCase())) { @@ -1570,7 +1574,7 @@ namespace ts.server { const result = project.getLanguageService().getEditsForRefactor( file, - this.formatSettings(file), + this.getFormatOptions(file), position || textRange, args.refactor, args.action @@ -1599,7 +1603,7 @@ namespace ts.server { private organizeImports({ scope }: protocol.OrganizeImportsRequestArgs, simplifiedResult: boolean): ReadonlyArray | ReadonlyArray { Debug.assert(scope.type === "file"); const { file, project } = this.getFileAndProject(scope.args); - const formatOptions = this.formatSettings(file); + const formatOptions = this.getFormatOptions(file); const changes = project.getLanguageService().organizeImports({ type: "file", fileName: file }, formatOptions); if (simplifiedResult) { return this.mapTextChangesToCodeEdits(project, changes); @@ -1617,9 +1621,8 @@ namespace ts.server { const scriptInfo = project.getScriptInfoForNormalizedPath(file); const { startPosition, endPosition } = this.getStartAndEndPosition(args, scriptInfo); - const formatOptions = this.formatSettings(file); - const codeActions = project.getLanguageService().getCodeFixesAtPosition(file, startPosition, endPosition, args.errorCodes, formatOptions); + const codeActions = project.getLanguageService().getCodeFixesAtPosition(file, startPosition, endPosition, args.errorCodes, this.getFormatOptions(file), this.getServicesOptions(file)); if (!codeActions) { return undefined; } @@ -1634,8 +1637,7 @@ namespace ts.server { private getCombinedCodeFix({ scope, fixId }: protocol.GetCombinedCodeFixRequestArgs, simplifiedResult: boolean): protocol.CombinedCodeActions | CombinedCodeActions { Debug.assert(scope.type === "file"); const { file, project } = this.getFileAndProject(scope.args); - const formatOptions = this.formatSettings(file); - const res = project.getLanguageService().getCombinedCodeFix({ type: "file", fileName: file }, fixId, formatOptions); + const res = project.getLanguageService().getCombinedCodeFix({ type: "file", fileName: file }, fixId, this.getFormatOptions(file), this.getServicesOptions(file)); if (simplifiedResult) { return { changes: this.mapTextChangesToCodeEdits(project, res.changes), commands: res.commands }; } @@ -2151,12 +2153,12 @@ namespace ts.server { } } - private formatSettings(file: NormalizedPath): FormatCodeSettings { + private getFormatOptions(file: NormalizedPath): FormatCodeSettings { return this.projectService.getFormatCodeOptions(file); } - private servicesSettings(file: NormalizedPath): ServicesSettings { - return this.projectService.getServicesSettings(file); + private getServicesOptions(file: NormalizedPath): Options { + return this.projectService.getOptions(file); } } diff --git a/src/services/codeFixProvider.ts b/src/services/codeFixProvider.ts index 502f8457d8c4a..e64d4ea65c70a 100644 --- a/src/services/codeFixProvider.ts +++ b/src/services/codeFixProvider.ts @@ -11,6 +11,7 @@ namespace ts { sourceFile: SourceFile; program: Program; cancellationToken: CancellationToken; + options: Options; } export interface CodeFixAllContext extends CodeFixContextBase { diff --git a/src/services/completions.ts b/src/services/completions.ts index fb03e63256603..f2fd38c40f12c 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -32,8 +32,7 @@ namespace ts.Completions { sourceFile: SourceFile, position: number, allSourceFiles: ReadonlyArray, - options: GetCompletionsAtPositionOptions, - settings: ServicesSettings, + options: Options, ): CompletionInfo | undefined { if (isInReferenceComment(sourceFile, position)) { const entries = PathCompletions.getTripleSlashReferenceCompletion(sourceFile, position, compilerOptions, host); @@ -45,7 +44,7 @@ namespace ts.Completions { if (isInString(sourceFile, position, contextToken)) { return !contextToken || !isStringLiteralLike(contextToken) ? undefined - : convertStringLiteralCompletions(getStringLiteralCompletionEntries(sourceFile, contextToken, position, typeChecker, compilerOptions, host), sourceFile, typeChecker, log, settings); + : convertStringLiteralCompletions(getStringLiteralCompletionEntries(sourceFile, contextToken, position, typeChecker, compilerOptions, host), sourceFile, typeChecker, log, options); } if (contextToken && isBreakOrContinueStatement(contextToken.parent) @@ -60,7 +59,7 @@ namespace ts.Completions { switch (completionData.kind) { case CompletionDataKind.Data: - return completionInfoFromData(sourceFile, typeChecker, compilerOptions, log, completionData, options.includeInsertTextCompletions, settings); + return completionInfoFromData(sourceFile, typeChecker, compilerOptions, log, completionData, options); case CompletionDataKind.JsDocTagName: // If the current position is a jsDoc tag name, only tag names should be provided for completion return jsdocCompletionInfo(JsDoc.getJSDocTagNameCompletions()); @@ -74,7 +73,7 @@ namespace ts.Completions { } } - function convertStringLiteralCompletions(completion: StringLiteralCompletion | undefined, sourceFile: SourceFile, checker: TypeChecker, log: Log, settings: ServicesSettings): CompletionInfo | undefined { + function convertStringLiteralCompletions(completion: StringLiteralCompletion | undefined, sourceFile: SourceFile, checker: TypeChecker, log: Log, options: Options): CompletionInfo | undefined { if (completion === undefined) { return undefined; } @@ -83,7 +82,7 @@ namespace ts.Completions { return convertPathCompletions(completion.paths); case StringLiteralCompletionKind.Properties: { const entries: CompletionEntry[] = []; - getCompletionEntriesFromSymbols(completion.symbols, entries, sourceFile, sourceFile, checker, ScriptTarget.ESNext, log, CompletionKind.String, settings); // Target will not be used, so arbitrary + getCompletionEntriesFromSymbols(completion.symbols, entries, sourceFile, sourceFile, checker, ScriptTarget.ESNext, log, CompletionKind.String, options); // Target will not be used, so arbitrary return { isGlobalCompletion: false, isMemberCompletion: true, isNewIdentifierLocation: true, entries }; } case StringLiteralCompletionKind.Types: { @@ -106,7 +105,7 @@ namespace ts.Completions { return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, entries }; } - function completionInfoFromData(sourceFile: SourceFile, typeChecker: TypeChecker, compilerOptions: CompilerOptions, log: Log, completionData: CompletionData, includeInsertTextCompletions: boolean, settings: ServicesSettings): CompletionInfo { + function completionInfoFromData(sourceFile: SourceFile, typeChecker: TypeChecker, compilerOptions: CompilerOptions, log: Log, completionData: CompletionData, options: Options): CompletionInfo { const { symbols, completionKind, isInSnippetScope, isNewIdentifierLocation, location, propertyAccessToConvert, keywordFilters, symbolToOriginInfoMap, recommendedCompletion, isJsxInitializer } = completionData; if (sourceFile.languageVariant === LanguageVariant.JSX && location && location.parent && isJsxClosingElement(location.parent)) { @@ -128,7 +127,7 @@ namespace ts.Completions { const entries: CompletionEntry[] = []; if (isSourceFileJavaScript(sourceFile)) { - const uniqueNames = getCompletionEntriesFromSymbols(symbols, entries, location, sourceFile, typeChecker, compilerOptions.target, log, completionKind, settings, includeInsertTextCompletions, propertyAccessToConvert, isJsxInitializer, recommendedCompletion, symbolToOriginInfoMap); + const uniqueNames = getCompletionEntriesFromSymbols(symbols, entries, location, sourceFile, typeChecker, compilerOptions.target, log, completionKind, options, propertyAccessToConvert, isJsxInitializer, recommendedCompletion, symbolToOriginInfoMap); getJavaScriptCompletionEntries(sourceFile, location.pos, uniqueNames, compilerOptions.target, entries); } else { @@ -136,7 +135,7 @@ namespace ts.Completions { return undefined; } - getCompletionEntriesFromSymbols(symbols, entries, location, sourceFile, typeChecker, compilerOptions.target, log, completionKind, settings, includeInsertTextCompletions, propertyAccessToConvert, isJsxInitializer, recommendedCompletion, symbolToOriginInfoMap); + getCompletionEntriesFromSymbols(symbols, entries, location, sourceFile, typeChecker, compilerOptions.target, log, completionKind, options, propertyAccessToConvert, isJsxInitializer, recommendedCompletion, symbolToOriginInfoMap); } // TODO add filter for keyword based on type/value/namespace and also location @@ -197,8 +196,7 @@ namespace ts.Completions { recommendedCompletion: Symbol | undefined, propertyAccessToConvert: PropertyAccessExpression | undefined, isJsxInitializer: IsJsxInitializer, - includeInsertTextCompletions: boolean, - settings: ServicesSettings, + options: Options, ): CompletionEntry | undefined { const info = getCompletionEntryDisplayNameForSymbol(symbol, target, origin, kind); if (!info) { @@ -208,12 +206,12 @@ namespace ts.Completions { let insertText: string | undefined; let replacementSpan: TextSpan | undefined; - if (includeInsertTextCompletions) { + if (options.includeInsertTextCompletions) { if (origin && origin.type === "this-type") { - insertText = needsConvertPropertyAccess ? `this[${quote(name, settings)}]` : `this.${name}`; + insertText = needsConvertPropertyAccess ? `this[${quote(name, options)}]` : `this.${name}`; } else if (needsConvertPropertyAccess) { - insertText = `[${quote(name, settings)}]`; + insertText = `[${quote(name, options)}]`; const dot = findChildOfKind(propertyAccessToConvert!, SyntaxKind.DotToken, sourceFile)!; // If the text after the '.' starts with this name, write over it. Else, add new text. const end = startsWith(name, propertyAccessToConvert!.name.text) ? propertyAccessToConvert!.name.end : dot.end; @@ -229,7 +227,7 @@ namespace ts.Completions { } } - if (insertText !== undefined && !includeInsertTextCompletions) { + if (insertText !== undefined && !options.includeInsertTextCompletions) { return undefined; } @@ -254,11 +252,17 @@ namespace ts.Completions { }; } - function quote(text: string, settings: ServicesSettings): string { + function quote(text: string, options: Options): string { const quoted = JSON.stringify(text); - return settings.quote === "'" - ? `'${stripQuotes(quoted).replace("'", "\\'").replace('\\"', '"')}'` - : quoted; + switch (options.quote) { + case undefined: + case "double": + return quoted; + case "single": + return `'${stripQuotes(quoted).replace("'", "\\'").replace('\\"', '"')}'`; + default: + return Debug.assertNever(options.quote); + } } function isRecommendedCompletionMatch(localSymbol: Symbol, recommendedCompletion: Symbol, checker: TypeChecker): boolean { @@ -283,8 +287,7 @@ namespace ts.Completions { target: ScriptTarget, log: Log, kind: CompletionKind, - settings: ServicesSettings, - includeInsertTextCompletions?: boolean, + options: Options, propertyAccessToConvert?: PropertyAccessExpression | undefined, isJsxInitializer?: IsJsxInitializer, recommendedCompletion?: Symbol, @@ -298,7 +301,7 @@ namespace ts.Completions { const uniques = createMap(); for (const symbol of symbols) { const origin = symbolToOriginInfoMap ? symbolToOriginInfoMap[getSymbolId(symbol)] : undefined; - const entry = createCompletionEntry(symbol, location, sourceFile, typeChecker, target, kind, origin, recommendedCompletion, propertyAccessToConvert, isJsxInitializer, includeInsertTextCompletions, settings); + const entry = createCompletionEntry(symbol, location, sourceFile, typeChecker, target, kind, origin, recommendedCompletion, propertyAccessToConvert, isJsxInitializer, options); if (!entry) { continue; } @@ -743,7 +746,7 @@ namespace ts.Completions { sourceFile: SourceFile, position: number, allSourceFiles: ReadonlyArray, - options: GetCompletionsAtPositionOptions, + options: Pick, target: ScriptTarget, ): CompletionData | Request | undefined { let start = timestamp(); diff --git a/src/services/services.ts b/src/services/services.ts index 3419a4f5798f7..15dc5aedd4976 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1424,7 +1424,7 @@ namespace ts { return [...program.getOptionsDiagnostics(cancellationToken), ...program.getGlobalDiagnostics(cancellationToken)]; } - function getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions = defaultCompletionOptions, settings: ServicesSettings = defaultServicesSettings): CompletionInfo { + function getCompletionsAtPosition(fileName: string, position: number, settings: Options = defaultOptions): CompletionInfo { synchronizeHostData(); return Completions.getCompletionsAtPosition( host, @@ -1434,7 +1434,6 @@ namespace ts { getValidSourceFile(fileName), position, program.getSourceFiles(), - options, settings); } @@ -1815,7 +1814,7 @@ namespace ts { return []; } - function getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings): ReadonlyArray { + function getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings, options: Options): ReadonlyArray { synchronizeHostData(); const sourceFile = getValidSourceFile(fileName); const span = createTextSpanFromBounds(start, end); @@ -1823,17 +1822,17 @@ namespace ts { return flatMap(deduplicate(errorCodes, equateValues, compareValues), errorCode => { cancellationToken.throwIfCancellationRequested(); - return codefix.getFixes({ errorCode, sourceFile, span, program, host, cancellationToken, formatContext }); + return codefix.getFixes({ errorCode, sourceFile, span, program, host, cancellationToken, formatContext, options }); }); } - function getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings): CombinedCodeActions { + function getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, options: Options): CombinedCodeActions { synchronizeHostData(); Debug.assert(scope.type === "file"); const sourceFile = getValidSourceFile(scope.fileName); const formatContext = formatting.getFormatContext(formatOptions); - return codefix.getAllFixes({ fixId, sourceFile, program, host, cancellationToken, formatContext }); + return codefix.getAllFixes({ fixId, sourceFile, program, host, cancellationToken, formatContext, options }); } function organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings): ReadonlyArray { diff --git a/src/services/shims.ts b/src/services/shims.ts index 120624d53d9ae..7d6ec740dad04 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -151,7 +151,7 @@ namespace ts { getEncodedSyntacticClassifications(fileName: string, start: number, length: number): string; getEncodedSemanticClassifications(fileName: string, start: number, length: number): string; - getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions | undefined, settings: ServicesSettings | undefined): string; + getCompletionsAtPosition(fileName: string, position: number, settings: Options | undefined): string; getCompletionEntryDetails(fileName: string, position: number, entryName: string, options: string/*Services.FormatCodeOptions*/ | undefined, source: string | undefined): string; getQuickInfoAtPosition(fileName: string, position: number): string; @@ -909,10 +909,10 @@ namespace ts { * to provide at the given source position and providing a member completion * list if requested. */ - public getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions | undefined, settings: ServicesSettings | undefined) { + public getCompletionsAtPosition(fileName: string, position: number, settings: Options | undefined) { return this.forwardJSONCall( - `getCompletionsAtPosition('${fileName}', ${position}, ${options})`, - () => this.languageService.getCompletionsAtPosition(fileName, position, options, settings) + `getCompletionsAtPosition('${fileName}', ${position}, ${settings})`, + () => this.languageService.getCompletionsAtPosition(fileName, position, settings) ); } diff --git a/src/services/types.ts b/src/services/types.ts index 77e055351ab05..76f5a38e26792 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -214,13 +214,13 @@ namespace ts { installPackage?(options: InstallPackageOptions): Promise; } - export interface ServicesSettings { - readonly quote: '"' | "'"; + export interface Options { + readonly quote?: "double" | "single"; + readonly includeExternalModuleExports?: boolean; + readonly includeInsertTextCompletions?: boolean; } /* @internal */ - export const defaultServicesSettings: ServicesSettings = { - quote: '"' - }; + export const defaultOptions: Options = {}; // // Public services of a language service instance associated @@ -250,7 +250,7 @@ namespace ts { getEncodedSyntacticClassifications(fileName: string, span: TextSpan): Classifications; getEncodedSemanticClassifications(fileName: string, span: TextSpan): Classifications; - getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions | undefined, settings: ServicesSettings | undefined): CompletionInfo; + getCompletionsAtPosition(fileName: string, position: number, settings: Options | undefined): CompletionInfo; // "options" and "source" are optional only for backwards-compatibility getCompletionEntryDetails( fileName: string, @@ -303,8 +303,8 @@ namespace ts { getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan; - getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings): ReadonlyArray; - getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings): CombinedCodeActions; + getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings, servicesOptions: Options): ReadonlyArray; + getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, servicesOptions: Options): CombinedCodeActions; applyCodeActionCommand(action: CodeActionCommand): Promise; applyCodeActionCommand(action: CodeActionCommand[]): Promise; applyCodeActionCommand(action: CodeActionCommand | CodeActionCommand[]): Promise; @@ -337,12 +337,8 @@ namespace ts { export type OrganizeImportsScope = CombinedCodeFixScope; - export interface GetCompletionsAtPositionOptions { - includeExternalModuleExports: boolean; - includeInsertTextCompletions: boolean; - } - /* @internal */ - export const defaultCompletionOptions: GetCompletionsAtPositionOptions = { includeExternalModuleExports: false, includeInsertTextCompletions: false }; + /** @deprecated Use Options */ + export type GetCompletionsAtPositionOptions = Options; export interface ApplyCodeActionCommandResult { successMessage: string; diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 635e79dcfd2f9..58913a0d1507a 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -4050,8 +4050,10 @@ declare namespace ts { isKnownTypesPackageName?(name: string): boolean; installPackage?(options: InstallPackageOptions): Promise; } - interface ServicesSettings { - readonly quote: '"' | "'"; + interface Options { + readonly quote?: "double" | "single"; + readonly includeExternalModuleExports?: boolean; + readonly includeInsertTextCompletions?: boolean; } interface LanguageService { cleanupSemanticCache(): void; @@ -4068,7 +4070,7 @@ declare namespace ts { getSemanticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[]; getEncodedSyntacticClassifications(fileName: string, span: TextSpan): Classifications; getEncodedSemanticClassifications(fileName: string, span: TextSpan): Classifications; - getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions | undefined, settings: ServicesSettings | undefined): CompletionInfo; + getCompletionsAtPosition(fileName: string, position: number, settings: Options | undefined): CompletionInfo; getCompletionEntryDetails(fileName: string, position: number, name: string, options: FormatCodeOptions | FormatCodeSettings | undefined, source: string | undefined): CompletionEntryDetails; getCompletionEntrySymbol(fileName: string, position: number, name: string, source: string | undefined): Symbol; getQuickInfoAtPosition(fileName: string, position: number): QuickInfo; @@ -4099,8 +4101,8 @@ declare namespace ts { getDocCommentTemplateAtPosition(fileName: string, position: number): TextInsertion; isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): boolean; getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan; - getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings): ReadonlyArray; - getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings): CombinedCodeActions; + getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings, servicesOptions: Options): ReadonlyArray; + getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, servicesOptions: Options): CombinedCodeActions; applyCodeActionCommand(action: CodeActionCommand): Promise; applyCodeActionCommand(action: CodeActionCommand[]): Promise; applyCodeActionCommand(action: CodeActionCommand | CodeActionCommand[]): Promise; @@ -4122,10 +4124,8 @@ declare namespace ts { fileName: string; } type OrganizeImportsScope = CombinedCodeFixScope; - interface GetCompletionsAtPositionOptions { - includeExternalModuleExports: boolean; - includeInsertTextCompletions: boolean; - } + /** @deprecated Use Options */ + type GetCompletionsAtPositionOptions = Options; interface ApplyCodeActionCommandResult { successMessage: string; } @@ -5914,7 +5914,7 @@ declare namespace ts.server.protocol { * The format options to use during formatting and other code editing features. */ formatOptions?: FormatCodeSettings; - servicesOptions?: ServicesSettings; + options?: Options; /** * The host's additional supported .js file extensions */ @@ -6287,15 +6287,13 @@ declare namespace ts.server.protocol { */ prefix?: string; /** - * If enabled, TypeScript will search through all external modules' exports and add them to the completions list. - * This affects lone identifier completions but not completions on the right hand side of `obj.`. + * @deprecated Use Options */ - includeExternalModuleExports: boolean; + includeExternalModuleExports?: boolean; /** - * If enabled, the completion list will include completions with invalid identifier names. - * For those entries, The `insertText` and `replacementSpan` properties will be set to change from `.x` property access to `["x"]`. + * @deprecated Use Options */ - includeInsertTextCompletions: boolean; + includeInsertTextCompletions?: boolean; } /** * Completions request; value of command field is "completions". @@ -7045,8 +7043,18 @@ declare namespace ts.server.protocol { placeOpenBraceOnNewLineForControlBlocks?: boolean; insertSpaceBeforeTypeAnnotation?: boolean; } - interface ServicesSettings { - quote: '"' | "'"; + interface Options { + quote: "double" | "single"; + /** + * If enabled, TypeScript will search through all external modules' exports and add them to the completions list. + * This affects lone identifier completions but not completions on the right hand side of `obj.`. + */ + includeExternalModuleExports: boolean; + /** + * If enabled, the completion list will include completions with invalid identifier names. + * For those entries, The `insertText` and `replacementSpan` properties will be set to change from `.x` property access to `["x"]`. + */ + includeInsertTextCompletions: boolean; } interface CompilerOptions { allowJs?: boolean; @@ -7310,8 +7318,8 @@ declare namespace ts.server { executeWithRequestId(requestId: number, f: () => T): T; executeCommand(request: protocol.Request): HandlerResponse; onMessage(message: string): void; - private formatSettings(file); - private servicesSettings(file); + private getFormatOptions(file); + private getServicesOptions(file); } interface HandlerResponse { response?: {}; @@ -7330,7 +7338,7 @@ declare namespace ts.server { */ readonly containingProjects: Project[]; private formatSettings; - private servicesSettings; + private options; private textStorage; constructor(host: ServerHost, fileName: NormalizedPath, scriptKind: ScriptKind, hasMixedContent: boolean, path: Path); isScriptOpen(): boolean; @@ -7339,14 +7347,14 @@ declare namespace ts.server { getSnapshot(): IScriptSnapshot; private ensureRealPath(); getFormatCodeSettings(): FormatCodeSettings; - getServicesSettings(): ServicesSettings; + getOptions(): Options; attachToProject(project: Project): boolean; isAttached(project: Project): boolean; detachFromProject(project: Project): void; detachAllProjects(): void; getDefaultProject(): Project; registerFileUpdate(): void; - setSettings(formatSettings: FormatCodeSettings, servicesSettings: ServicesSettings): void; + setOptions(formatSettings: FormatCodeSettings, options: Options): void; getLatestVersion(): string; saveTo(fileName: string): void; reloadFromFile(tempFileName?: NormalizedPath): void; @@ -7724,7 +7732,7 @@ declare namespace ts.server { function convertScriptKindName(scriptKindName: protocol.ScriptKindName): ScriptKind.Unknown | ScriptKind.JS | ScriptKind.JSX | ScriptKind.TS | ScriptKind.TSX; interface HostConfiguration { formatCodeOptions: FormatCodeSettings; - servicesOptions: ServicesSettings; + options: Options; hostInfo: string; extraFileExtensions?: JsFileExtensionInfo[]; } @@ -7835,7 +7843,7 @@ declare namespace ts.server { private ensureProjectStructuresUptoDate(); private updateProjectIfDirty(project); getFormatCodeOptions(file: NormalizedPath): FormatCodeSettings; - getServicesSettings(file: NormalizedPath): ServicesSettings; + getOptions(file: NormalizedPath): Options; private onSourceFileChanged(fileName, eventKind); private handleDeletedFile(info); private onConfigChangedForConfiguredProject(project, eventKind); diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index ab09b1afce88b..ab24c6326ec2b 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -4302,8 +4302,10 @@ declare namespace ts { isKnownTypesPackageName?(name: string): boolean; installPackage?(options: InstallPackageOptions): Promise; } - interface ServicesSettings { - readonly quote: '"' | "'"; + interface Options { + readonly quote?: "double" | "single"; + readonly includeExternalModuleExports?: boolean; + readonly includeInsertTextCompletions?: boolean; } interface LanguageService { cleanupSemanticCache(): void; @@ -4320,7 +4322,7 @@ declare namespace ts { getSemanticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[]; getEncodedSyntacticClassifications(fileName: string, span: TextSpan): Classifications; getEncodedSemanticClassifications(fileName: string, span: TextSpan): Classifications; - getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions | undefined, settings: ServicesSettings | undefined): CompletionInfo; + getCompletionsAtPosition(fileName: string, position: number, settings: Options | undefined): CompletionInfo; getCompletionEntryDetails(fileName: string, position: number, name: string, options: FormatCodeOptions | FormatCodeSettings | undefined, source: string | undefined): CompletionEntryDetails; getCompletionEntrySymbol(fileName: string, position: number, name: string, source: string | undefined): Symbol; getQuickInfoAtPosition(fileName: string, position: number): QuickInfo; @@ -4351,8 +4353,8 @@ declare namespace ts { getDocCommentTemplateAtPosition(fileName: string, position: number): TextInsertion; isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): boolean; getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan; - getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings): ReadonlyArray; - getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings): CombinedCodeActions; + getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings, servicesOptions: Options): ReadonlyArray; + getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, servicesOptions: Options): CombinedCodeActions; applyCodeActionCommand(action: CodeActionCommand): Promise; applyCodeActionCommand(action: CodeActionCommand[]): Promise; applyCodeActionCommand(action: CodeActionCommand | CodeActionCommand[]): Promise; @@ -4374,10 +4376,8 @@ declare namespace ts { fileName: string; } type OrganizeImportsScope = CombinedCodeFixScope; - interface GetCompletionsAtPositionOptions { - includeExternalModuleExports: boolean; - includeInsertTextCompletions: boolean; - } + /** @deprecated Use Options */ + type GetCompletionsAtPositionOptions = Options; interface ApplyCodeActionCommandResult { successMessage: string; } diff --git a/tests/cases/fourslash/completionListInvalidMemberNames_escapeQuote.ts b/tests/cases/fourslash/completionListInvalidMemberNames_escapeQuote.ts index 9af041b2f7db5..7f196adb1223f 100644 --- a/tests/cases/fourslash/completionListInvalidMemberNames_escapeQuote.ts +++ b/tests/cases/fourslash/completionListInvalidMemberNames_escapeQuote.ts @@ -8,7 +8,5 @@ verify.completionsAt("", [{ name: `"'`, insertText: `["\\"'"]`, replacementSpan verify.completionsAt("", [{ name: `"'`, insertText: `['"\\'']`, replacementSpan }], { includeInsertTextCompletions: true, - settings: { - quote: "'", - }, + quote: "single", }); diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index af3e08431935a..6d81f0cb64c3b 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -151,9 +151,7 @@ declare namespace FourSlashInterface { kind?: string | { kind?: string, kindModifiers?: string }, spanIndex?: number, hasAction?: boolean, - options?: { - includeExternalModuleExports?: boolean, - includeInsertTextCompletions?: boolean, + options?: Options & { sourceDisplay?: string, isRecommended?: true, insertText?: string, @@ -196,11 +194,7 @@ declare namespace FourSlashInterface { class verify extends verifyNegatable { assertHasRanges(ranges: Range[]): void; caretAtMarker(markerName?: string): void; - completionsAt(markerName: string, completions: ReadonlyArray, options?: { - isNewIdentifierLocation?: boolean; - includeInsertTextCompletions?: boolean; - settings?: ServicesSettings; - }): void; + completionsAt(markerName: string, completions: ReadonlyArray, options?: CompletionsAtOptions): void; completionsAndDetailsAt( markerName: string, completions: { @@ -528,8 +522,12 @@ declare namespace FourSlashInterface { category: string; code: number; } - interface ServicesSettings { - quote?: '"' | "'"; + interface Options { + quote?: "double" | "single"; + includeInsertTextCompletions?: boolean; + } + interface CompletionsAtOptions extends Options { + isNewIdentifierLocation?: boolean; } } declare function verifyOperationIsCancelled(f: any): void; From 4d592eea8b43c092ebfa05926c9c13602e9dcc5e Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 6 Mar 2018 07:46:49 -0800 Subject: [PATCH 03/10] More review --- src/harness/fourslash.ts | 16 ++++++++-------- src/harness/unittests/extractTestHelpers.ts | 2 ++ src/harness/unittests/organizeImports.ts | 4 ++-- src/server/protocol.ts | 6 +++--- src/server/session.ts | 16 ++++++++-------- src/services/organizeImports.ts | 4 +++- src/services/refactorProvider.ts | 1 + src/services/services.ts | 17 ++++++++++------- src/services/types.ts | 10 +++++----- .../reference/api/tsserverlibrary.d.ts | 18 +++++++++--------- tests/baselines/reference/api/typescript.d.ts | 10 +++++----- 11 files changed, 56 insertions(+), 48 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index cd8132760ceb9..7966750c8e9b3 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -2924,7 +2924,7 @@ Actual: ${stringify(fullActual)}`); public verifyApplicableRefactorAvailableAtMarker(negative: boolean, markerName: string) { const marker = this.getMarkerByName(markerName); - const applicableRefactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, marker.position); + const applicableRefactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, marker.position, ts.defaultOptions); const isAvailable = applicableRefactors && applicableRefactors.length > 0; if (negative && isAvailable) { this.raiseError(`verifyApplicableRefactorAvailableAtMarker failed - expected no refactor at marker ${markerName} but found some.`); @@ -2944,7 +2944,7 @@ Actual: ${stringify(fullActual)}`); public verifyRefactorAvailable(negative: boolean, name: string, actionName?: string) { const selection = this.getSelection(); - let refactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, selection) || []; + let refactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, selection, ts.defaultOptions) || []; refactors = refactors.filter(r => r.name === name && (actionName === undefined || r.actions.some(a => a.name === actionName))); const isAvailable = refactors.length > 0; @@ -2966,7 +2966,7 @@ Actual: ${stringify(fullActual)}`); public verifyRefactor({ name, actionName, refactors }: FourSlashInterface.VerifyRefactorOptions) { const selection = this.getSelection(); - const actualRefactors = (this.languageService.getApplicableRefactors(this.activeFile.fileName, selection) || ts.emptyArray) + const actualRefactors = (this.languageService.getApplicableRefactors(this.activeFile.fileName, selection, ts.defaultOptions) || ts.emptyArray) .filter(r => r.name === name && r.actions.some(a => a.name === actionName)); this.assertObjectsEqual(actualRefactors, refactors); } @@ -2977,7 +2977,7 @@ Actual: ${stringify(fullActual)}`); throw new Error("Exactly one refactor range is allowed per test."); } - const applicableRefactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, { pos: ranges[0].pos, end: ranges[0].end }); + const applicableRefactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, ts.first(ranges), ts.defaultOptions); const isAvailable = applicableRefactors && applicableRefactors.length > 0; if (negative && isAvailable) { this.raiseError(`verifyApplicableRefactorAvailableForRange failed - expected no refactor but found some.`); @@ -2989,7 +2989,7 @@ Actual: ${stringify(fullActual)}`); public applyRefactor({ refactorName, actionName, actionDescription, newContent: newContentWithRenameMarker }: FourSlashInterface.ApplyRefactorOptions) { const range = this.getSelection(); - const refactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, range); + const refactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, range, ts.defaultOptions); const refactorsWithName = refactors.filter(r => r.name === refactorName); if (refactorsWithName.length === 0) { this.raiseError(`The expected refactor: ${refactorName} is not available at the marker location.\nAvailable refactors: ${refactors.map(r => r.name)}`); @@ -3003,7 +3003,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); + const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, this.formatCodeSettings, range, refactorName, actionName, ts.defaultOptions); for (const edit of editInfo.edits) { this.applyEdits(edit.fileName, edit.textChanges, /*isFormattingEdit*/ false); } @@ -3048,14 +3048,14 @@ Actual: ${stringify(fullActual)}`); formattingOptions = formattingOptions || this.formatCodeSettings; const markerPos = this.getMarkerByName(markerName).position; - const applicableRefactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, markerPos); + const applicableRefactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, markerPos, ts.defaultOptions); 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); + const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, formattingOptions, markerPos, refactorNameToApply, actionName, ts.defaultOptions); for (const edit of editInfo.edits) { this.applyEdits(edit.fileName, edit.textChanges, /*isFormattingEdit*/ false); diff --git a/src/harness/unittests/extractTestHelpers.ts b/src/harness/unittests/extractTestHelpers.ts index 8970990326c15..dc9a65b92ecbf 100644 --- a/src/harness/unittests/extractTestHelpers.ts +++ b/src/harness/unittests/extractTestHelpers.ts @@ -127,6 +127,7 @@ namespace ts { endPosition: selectionRange.end, host: notImplementedHost, formatContext: formatting.getFormatContext(testFormatOptions), + options: defaultOptions, }; const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromRange(selectionRange)); assert.equal(rangeToExtract.errors, undefined, rangeToExtract.errors && "Range error: " + rangeToExtract.errors[0].messageText); @@ -190,6 +191,7 @@ namespace ts { endPosition: selectionRange.end, host: notImplementedHost, formatContext: formatting.getFormatContext(testFormatOptions), + options: defaultOptions, }; const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromRange(selectionRange)); assert.isUndefined(rangeToExtract.errors, rangeToExtract.errors && "Range error: " + rangeToExtract.errors[0].messageText); diff --git a/src/harness/unittests/organizeImports.ts b/src/harness/unittests/organizeImports.ts index bb5c9cbee1867..b11f35be7683f 100644 --- a/src/harness/unittests/organizeImports.ts +++ b/src/harness/unittests/organizeImports.ts @@ -193,7 +193,7 @@ export const Other = 1; content: "function F() { }", }; const languageService = makeLanguageService(testFile); - const changes = languageService.organizeImports({ type: "file", fileName: testFile.path }, testFormatOptions); + const changes = languageService.organizeImports({ type: "file", fileName: testFile.path }, testFormatOptions, defaultOptions); assert.isEmpty(changes); }); @@ -403,7 +403,7 @@ import { React, Other } from "react"; function runBaseline(baselinePath: string, testFile: TestFSWithWatch.FileOrFolder, ...otherFiles: TestFSWithWatch.FileOrFolder[]) { const { path: testPath, content: testContent } = testFile; const languageService = makeLanguageService(testFile, ...otherFiles); - const changes = languageService.organizeImports({ type: "file", fileName: testPath }, testFormatOptions); + const changes = languageService.organizeImports({ type: "file", fileName: testPath }, testFormatOptions, defaultOptions); assert.equal(changes.length, 1); assert.equal(changes[0].fileName, testPath); diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 58aa25ed56daa..ceeaef3b9961e 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -2589,17 +2589,17 @@ namespace ts.server.protocol { } export interface Options { - quote: "double" | "single"; + quote?: "double" | "single"; /** * If enabled, TypeScript will search through all external modules' exports and add them to the completions list. * This affects lone identifier completions but not completions on the right hand side of `obj.`. */ - includeExternalModuleExports: boolean; + includeExternalModuleExports?: boolean; /** * If enabled, the completion list will include completions with invalid identifier names. * For those entries, The `insertText` and `replacementSpan` properties will be set to change from `.x` property access to `["x"]`. */ - includeInsertTextCompletions: boolean; + includeInsertTextCompletions?: boolean; } export interface CompilerOptions { diff --git a/src/server/session.ts b/src/server/session.ts index 0b20f829f125d..cd51c7e2aaba1 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1239,7 +1239,7 @@ namespace ts.server { const position = this.getPosition(args, scriptInfo); const completions = project.getLanguageService().getCompletionsAtPosition(file, position, { - ...this.getServicesOptions(file), + ...this.getOptions(file), includeExternalModuleExports: args.includeExternalModuleExports, includeInsertTextCompletions: args.includeInsertTextCompletions }); @@ -1564,7 +1564,7 @@ namespace ts.server { const { file, project } = this.getFileAndProject(args); const scriptInfo = project.getScriptInfoForNormalizedPath(file); const { position, textRange } = this.extractPositionAndRange(args, scriptInfo); - return project.getLanguageService().getApplicableRefactors(file, position || textRange); + return project.getLanguageService().getApplicableRefactors(file, position || textRange, this.getOptions(file)); } private getEditsForRefactor(args: protocol.GetEditsForRefactorRequestArgs, simplifiedResult: boolean): RefactorEditInfo | protocol.RefactorEditInfo { @@ -1577,7 +1577,8 @@ namespace ts.server { this.getFormatOptions(file), position || textRange, args.refactor, - args.action + args.action, + this.getOptions(file), ); if (result === undefined) { @@ -1603,8 +1604,7 @@ namespace ts.server { private organizeImports({ scope }: protocol.OrganizeImportsRequestArgs, simplifiedResult: boolean): ReadonlyArray | ReadonlyArray { Debug.assert(scope.type === "file"); const { file, project } = this.getFileAndProject(scope.args); - const formatOptions = this.getFormatOptions(file); - const changes = project.getLanguageService().organizeImports({ type: "file", fileName: file }, formatOptions); + const changes = project.getLanguageService().organizeImports({ type: "file", fileName: file }, this.getFormatOptions(file), this.getOptions(file)); if (simplifiedResult) { return this.mapTextChangesToCodeEdits(project, changes); } @@ -1622,7 +1622,7 @@ namespace ts.server { const scriptInfo = project.getScriptInfoForNormalizedPath(file); const { startPosition, endPosition } = this.getStartAndEndPosition(args, scriptInfo); - const codeActions = project.getLanguageService().getCodeFixesAtPosition(file, startPosition, endPosition, args.errorCodes, this.getFormatOptions(file), this.getServicesOptions(file)); + const codeActions = project.getLanguageService().getCodeFixesAtPosition(file, startPosition, endPosition, args.errorCodes, this.getFormatOptions(file), this.getOptions(file)); if (!codeActions) { return undefined; } @@ -1637,7 +1637,7 @@ namespace ts.server { private getCombinedCodeFix({ scope, fixId }: protocol.GetCombinedCodeFixRequestArgs, simplifiedResult: boolean): protocol.CombinedCodeActions | CombinedCodeActions { Debug.assert(scope.type === "file"); const { file, project } = this.getFileAndProject(scope.args); - const res = project.getLanguageService().getCombinedCodeFix({ type: "file", fileName: file }, fixId, this.getFormatOptions(file), this.getServicesOptions(file)); + const res = project.getLanguageService().getCombinedCodeFix({ type: "file", fileName: file }, fixId, this.getFormatOptions(file), this.getOptions(file)); if (simplifiedResult) { return { changes: this.mapTextChangesToCodeEdits(project, res.changes), commands: res.commands }; } @@ -2157,7 +2157,7 @@ namespace ts.server { return this.projectService.getFormatCodeOptions(file); } - private getServicesOptions(file: NormalizedPath): Options { + private getOptions(file: NormalizedPath): Options { return this.projectService.getOptions(file); } } diff --git a/src/services/organizeImports.ts b/src/services/organizeImports.ts index 511fa821fd231..eb6fe5174fd9b 100644 --- a/src/services/organizeImports.ts +++ b/src/services/organizeImports.ts @@ -11,7 +11,9 @@ namespace ts.OrganizeImports { sourceFile: SourceFile, formatContext: formatting.FormatContext, host: LanguageServiceHost, - program: Program) { + program: Program, + _options: Options, + ) { const changeTracker = textChanges.ChangeTracker.fromContext({ host, formatContext }); diff --git a/src/services/refactorProvider.ts b/src/services/refactorProvider.ts index 3d5957c694c99..10cba3c3174d2 100644 --- a/src/services/refactorProvider.ts +++ b/src/services/refactorProvider.ts @@ -14,6 +14,7 @@ namespace ts { endPosition?: number; program: Program; cancellationToken?: CancellationToken; + options: Options; } export namespace refactor { diff --git a/src/services/services.ts b/src/services/services.ts index 15dc5aedd4976..c5a197059a6f4 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1835,13 +1835,13 @@ namespace ts { return codefix.getAllFixes({ fixId, sourceFile, program, host, cancellationToken, formatContext, options }); } - function organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings): ReadonlyArray { + function organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, options: Options): ReadonlyArray { synchronizeHostData(); Debug.assert(scope.type === "file"); const sourceFile = getValidSourceFile(scope.fileName); const formatContext = formatting.getFormatContext(formatOptions); - return OrganizeImports.organizeImports(sourceFile, formatContext, host, program); + return OrganizeImports.organizeImports(sourceFile, formatContext, host, program, options); } function applyCodeActionCommand(action: CodeActionCommand): Promise; @@ -2065,7 +2065,7 @@ namespace ts { return Rename.getRenameInfo(program.getTypeChecker(), defaultLibFileName, getCanonicalFileName, getValidSourceFile(fileName), position); } - function getRefactorContext(file: SourceFile, positionOrRange: number | TextRange, formatOptions?: FormatCodeSettings): RefactorContext { + function getRefactorContext(file: SourceFile, positionOrRange: number | TextRange, options: Options, formatOptions?: FormatCodeSettings): RefactorContext { const [startPosition, endPosition] = typeof positionOrRange === "number" ? [positionOrRange, undefined] : [positionOrRange.pos, positionOrRange.end]; return { file, @@ -2075,13 +2075,14 @@ namespace ts { host, formatContext: formatting.getFormatContext(formatOptions), cancellationToken, + options, }; } - function getApplicableRefactors(fileName: string, positionOrRange: number | TextRange): ApplicableRefactorInfo[] { + function getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, options: Options): ApplicableRefactorInfo[] { synchronizeHostData(); const file = getValidSourceFile(fileName); - return refactor.getApplicableRefactors(getRefactorContext(file, positionOrRange)); + return refactor.getApplicableRefactors(getRefactorContext(file, positionOrRange, options)); } function getEditsForRefactor( @@ -2089,11 +2090,13 @@ namespace ts { formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, - actionName: string): RefactorEditInfo { + actionName: string, + options: Options, + ): RefactorEditInfo { synchronizeHostData(); const file = getValidSourceFile(fileName); - return refactor.getEditsForRefactor(getRefactorContext(file, positionOrRange, formatOptions), refactorName, actionName); + return refactor.getEditsForRefactor(getRefactorContext(file, positionOrRange, options, formatOptions), refactorName, actionName); } return { diff --git a/src/services/types.ts b/src/services/types.ts index 76f5a38e26792..58255b1c40e43 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -303,8 +303,8 @@ namespace ts { getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan; - getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings, servicesOptions: Options): ReadonlyArray; - getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, servicesOptions: Options): CombinedCodeActions; + getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings, options: Options): ReadonlyArray; + getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, options: Options): CombinedCodeActions; applyCodeActionCommand(action: CodeActionCommand): Promise; applyCodeActionCommand(action: CodeActionCommand[]): Promise; applyCodeActionCommand(action: CodeActionCommand | CodeActionCommand[]): Promise; @@ -314,9 +314,9 @@ namespace ts { applyCodeActionCommand(fileName: string, action: CodeActionCommand[]): Promise; /** @deprecated `fileName` will be ignored */ applyCodeActionCommand(fileName: string, action: CodeActionCommand | CodeActionCommand[]): Promise; - getApplicableRefactors(fileName: string, positionOrRaneg: number | TextRange): ApplicableRefactorInfo[]; - getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string): RefactorEditInfo | undefined; - organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings): ReadonlyArray; + getApplicableRefactors(fileName: string, positionOrRaneg: number | TextRange, options: Options): ApplicableRefactorInfo[]; + getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string, options: Options): RefactorEditInfo | undefined; + organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, options: Options): ReadonlyArray; getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean): EmitOutput; diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 58913a0d1507a..a90d83b44f73a 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -4101,8 +4101,8 @@ declare namespace ts { getDocCommentTemplateAtPosition(fileName: string, position: number): TextInsertion; isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): boolean; getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan; - getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings, servicesOptions: Options): ReadonlyArray; - getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, servicesOptions: Options): CombinedCodeActions; + getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings, options: Options): ReadonlyArray; + getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, options: Options): CombinedCodeActions; applyCodeActionCommand(action: CodeActionCommand): Promise; applyCodeActionCommand(action: CodeActionCommand[]): Promise; applyCodeActionCommand(action: CodeActionCommand | CodeActionCommand[]): Promise; @@ -4112,9 +4112,9 @@ declare namespace ts { applyCodeActionCommand(fileName: string, action: CodeActionCommand[]): Promise; /** @deprecated `fileName` will be ignored */ applyCodeActionCommand(fileName: string, action: CodeActionCommand | CodeActionCommand[]): Promise; - getApplicableRefactors(fileName: string, positionOrRaneg: number | TextRange): ApplicableRefactorInfo[]; - getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string): RefactorEditInfo | undefined; - organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings): ReadonlyArray; + getApplicableRefactors(fileName: string, positionOrRaneg: number | TextRange, options: Options): ApplicableRefactorInfo[]; + getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string, options: Options): RefactorEditInfo | undefined; + organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, options: Options): ReadonlyArray; getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean): EmitOutput; getProgram(): Program; dispose(): void; @@ -7044,17 +7044,17 @@ declare namespace ts.server.protocol { insertSpaceBeforeTypeAnnotation?: boolean; } interface Options { - quote: "double" | "single"; + quote?: "double" | "single"; /** * If enabled, TypeScript will search through all external modules' exports and add them to the completions list. * This affects lone identifier completions but not completions on the right hand side of `obj.`. */ - includeExternalModuleExports: boolean; + includeExternalModuleExports?: boolean; /** * If enabled, the completion list will include completions with invalid identifier names. * For those entries, The `insertText` and `replacementSpan` properties will be set to change from `.x` property access to `["x"]`. */ - includeInsertTextCompletions: boolean; + includeInsertTextCompletions?: boolean; } interface CompilerOptions { allowJs?: boolean; @@ -7319,7 +7319,7 @@ declare namespace ts.server { executeCommand(request: protocol.Request): HandlerResponse; onMessage(message: string): void; private getFormatOptions(file); - private getServicesOptions(file); + private getOptions(file); } interface HandlerResponse { response?: {}; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index ab24c6326ec2b..51cc267a195bb 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -4353,8 +4353,8 @@ declare namespace ts { getDocCommentTemplateAtPosition(fileName: string, position: number): TextInsertion; isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): boolean; getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan; - getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings, servicesOptions: Options): ReadonlyArray; - getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, servicesOptions: Options): CombinedCodeActions; + getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings, options: Options): ReadonlyArray; + getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, options: Options): CombinedCodeActions; applyCodeActionCommand(action: CodeActionCommand): Promise; applyCodeActionCommand(action: CodeActionCommand[]): Promise; applyCodeActionCommand(action: CodeActionCommand | CodeActionCommand[]): Promise; @@ -4364,9 +4364,9 @@ declare namespace ts { applyCodeActionCommand(fileName: string, action: CodeActionCommand[]): Promise; /** @deprecated `fileName` will be ignored */ applyCodeActionCommand(fileName: string, action: CodeActionCommand | CodeActionCommand[]): Promise; - getApplicableRefactors(fileName: string, positionOrRaneg: number | TextRange): ApplicableRefactorInfo[]; - getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string): RefactorEditInfo | undefined; - organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings): ReadonlyArray; + getApplicableRefactors(fileName: string, positionOrRaneg: number | TextRange, options: Options): ApplicableRefactorInfo[]; + getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string, options: Options): RefactorEditInfo | undefined; + organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, options: Options): ReadonlyArray; getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean): EmitOutput; getProgram(): Program; dispose(): void; From 327d6889dc0db8e8a34e0eb3a0ef12cba46defec Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 6 Mar 2018 13:09:03 -0800 Subject: [PATCH 04/10] Use different names for Options and GetCompletionsAtPositionOptions (todo: come up with better names) --- src/harness/fourslash.ts | 2 +- src/server/protocol.ts | 6 ++--- src/services/completions.ts | 12 ++++----- src/services/services.ts | 22 +++++++++------ src/services/types.ts | 19 ++++++++----- .../reference/api/tsserverlibrary.d.ts | 27 +++++++++++-------- tests/baselines/reference/api/typescript.d.ts | 21 +++++++++------ 7 files changed, 65 insertions(+), 44 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 7966750c8e9b3..436ba364cfb5b 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -2392,7 +2392,7 @@ Actual: ${stringify(fullActual)}`); public applyCodeActionFromCompletion(markerName: string, options: FourSlashInterface.VerifyCompletionActionOptions) { this.goToMarker(markerName); - const actualCompletion = this.getCompletionListAtCaret({ ...ts.defaultOptions, includeExternalModuleExports: true }).entries.find(e => + const actualCompletion = this.getCompletionListAtCaret({ ...ts.defaultOptions, includeExternalModuleExportsInCompletionList: true }).entries.find(e => e.name === options.name && e.source === options.source); if (!actualCompletion.hasAction) { diff --git a/src/server/protocol.ts b/src/server/protocol.ts index ceeaef3b9961e..4f9107627a554 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -2589,17 +2589,17 @@ namespace ts.server.protocol { } export interface Options { - quote?: "double" | "single"; + readonly quote?: "double" | "single"; /** * If enabled, TypeScript will search through all external modules' exports and add them to the completions list. * This affects lone identifier completions but not completions on the right hand side of `obj.`. */ - includeExternalModuleExports?: boolean; + readonly includeExternalModuleExportsInCompletionList?: boolean; /** * If enabled, the completion list will include completions with invalid identifier names. * For those entries, The `insertText` and `replacementSpan` properties will be set to change from `.x` property access to `["x"]`. */ - includeInsertTextCompletions?: boolean; + readonly includeInsertTextCompletionsInCompletionList?: boolean; } export interface CompilerOptions { diff --git a/src/services/completions.ts b/src/services/completions.ts index f2fd38c40f12c..0c33af7619541 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -206,7 +206,7 @@ namespace ts.Completions { let insertText: string | undefined; let replacementSpan: TextSpan | undefined; - if (options.includeInsertTextCompletions) { + if (options.includeInsertTextCompletionsInCompletionList) { if (origin && origin.type === "this-type") { insertText = needsConvertPropertyAccess ? `this[${quote(name, options)}]` : `this.${name}`; } @@ -227,7 +227,7 @@ namespace ts.Completions { } } - if (insertText !== undefined && !options.includeInsertTextCompletions) { + if (insertText !== undefined && !options.includeInsertTextCompletionsInCompletionList) { return undefined; } @@ -479,7 +479,7 @@ namespace ts.Completions { { name, source }: CompletionEntryIdentifier, allSourceFiles: ReadonlyArray, ): SymbolCompletion | { type: "request", request: Request } | { type: "none" } { - const completionData = getCompletionData(typeChecker, log, sourceFile, position, allSourceFiles, { includeExternalModuleExports: true, includeInsertTextCompletions: true }, compilerOptions.target); + const completionData = getCompletionData(typeChecker, log, sourceFile, position, allSourceFiles, { includeExternalModuleExportsInCompletionList: true, includeInsertTextCompletionsInCompletionList: true }, compilerOptions.target); if (!completionData) { return { type: "none" }; } @@ -746,7 +746,7 @@ namespace ts.Completions { sourceFile: SourceFile, position: number, allSourceFiles: ReadonlyArray, - options: Pick, + options: Pick, target: ScriptTarget, ): CompletionData | Request | undefined { let start = timestamp(); @@ -1150,7 +1150,7 @@ namespace ts.Completions { symbols = Debug.assertEachDefined(typeChecker.getSymbolsInScope(scopeNode, symbolMeanings), "getSymbolsInScope() should all be defined"); // Need to insert 'this.' before properties of `this` type, so only do that if `includeInsertTextCompletions` - if (options.includeInsertTextCompletions && scopeNode.kind !== SyntaxKind.SourceFile) { + if (options.includeInsertTextCompletionsInCompletionList && scopeNode.kind !== SyntaxKind.SourceFile) { const thisType = typeChecker.tryGetThisTypeAt(scopeNode); if (thisType) { for (const symbol of getPropertiesForCompletion(thisType, typeChecker, /*isForAccess*/ true)) { @@ -1160,7 +1160,7 @@ namespace ts.Completions { } } - if (options.includeExternalModuleExports) { + if (options.includeExternalModuleExportsInCompletionList) { getSymbolsFromOtherSourceFileExports(symbols, previousToken && isIdentifier(previousToken) ? previousToken.text : "", target); } filterGlobalCompletion(symbols); diff --git a/src/services/services.ts b/src/services/services.ts index c5a197059a6f4..ac9efb99d0a58 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -32,7 +32,7 @@ namespace ts { /** The version of the language service API */ - export const servicesVersion = "0.7"; + export const servicesVersion = "0.8"; function createNode(kind: TKind, pos: number, end: number, parent?: Node): NodeObject | TokenObject | IdentifierObject { const node = isNodeKind(kind) ? new NodeObject(kind, pos, end) : @@ -1424,7 +1424,13 @@ namespace ts { return [...program.getOptionsDiagnostics(cancellationToken), ...program.getGlobalDiagnostics(cancellationToken)]; } - function getCompletionsAtPosition(fileName: string, position: number, settings: Options = defaultOptions): CompletionInfo { + function getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions = defaultOptions): CompletionInfo { + // Convert from deprecated options names to new names + const fullOptions: Options = { + ...identity(options), // avoid excess property check + includeExternalModuleExportsInCompletionList: options.includeExternalModuleExportsInCompletionList || options.includeExternalModuleExports, + includeInsertTextCompletionsInCompletionList: options.includeInsertTextCompletionsInCompletionList || options.includeInsertTextCompletions, + }; synchronizeHostData(); return Completions.getCompletionsAtPosition( host, @@ -1434,7 +1440,7 @@ namespace ts { getValidSourceFile(fileName), position, program.getSourceFiles(), - settings); + fullOptions); } function getCompletionEntryDetails(fileName: string, position: number, name: string, formattingOptions?: FormatCodeSettings, source?: string): CompletionEntryDetails { @@ -1814,7 +1820,7 @@ namespace ts { return []; } - function getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings, options: Options): ReadonlyArray { + function getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings, options: Options = defaultOptions): ReadonlyArray { synchronizeHostData(); const sourceFile = getValidSourceFile(fileName); const span = createTextSpanFromBounds(start, end); @@ -1826,7 +1832,7 @@ namespace ts { }); } - function getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, options: Options): CombinedCodeActions { + function getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, options: Options = defaultOptions): CombinedCodeActions { synchronizeHostData(); Debug.assert(scope.type === "file"); const sourceFile = getValidSourceFile(scope.fileName); @@ -1835,7 +1841,7 @@ namespace ts { return codefix.getAllFixes({ fixId, sourceFile, program, host, cancellationToken, formatContext, options }); } - function organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, options: Options): ReadonlyArray { + function organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, options: Options = defaultOptions): ReadonlyArray { synchronizeHostData(); Debug.assert(scope.type === "file"); const sourceFile = getValidSourceFile(scope.fileName); @@ -2079,7 +2085,7 @@ namespace ts { }; } - function getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, options: Options): ApplicableRefactorInfo[] { + function getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, options: Options = defaultOptions): ApplicableRefactorInfo[] { synchronizeHostData(); const file = getValidSourceFile(fileName); return refactor.getApplicableRefactors(getRefactorContext(file, positionOrRange, options)); @@ -2091,7 +2097,7 @@ namespace ts { positionOrRange: number | TextRange, refactorName: string, actionName: string, - options: Options, + options: Options = defaultOptions, ): RefactorEditInfo { synchronizeHostData(); diff --git a/src/services/types.ts b/src/services/types.ts index 58255b1c40e43..0d646d47564f6 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -216,8 +216,8 @@ namespace ts { export interface Options { readonly quote?: "double" | "single"; - readonly includeExternalModuleExports?: boolean; - readonly includeInsertTextCompletions?: boolean; + readonly includeExternalModuleExportsInCompletionList?: boolean; + readonly includeInsertTextCompletionsInCompletionList?: boolean; } /* @internal */ export const defaultOptions: Options = {}; @@ -250,7 +250,7 @@ namespace ts { getEncodedSyntacticClassifications(fileName: string, span: TextSpan): Classifications; getEncodedSemanticClassifications(fileName: string, span: TextSpan): Classifications; - getCompletionsAtPosition(fileName: string, position: number, settings: Options | undefined): CompletionInfo; + getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions | undefined): CompletionInfo; // "options" and "source" are optional only for backwards-compatibility getCompletionEntryDetails( fileName: string, @@ -314,9 +314,9 @@ namespace ts { applyCodeActionCommand(fileName: string, action: CodeActionCommand[]): Promise; /** @deprecated `fileName` will be ignored */ applyCodeActionCommand(fileName: string, action: CodeActionCommand | CodeActionCommand[]): Promise; - getApplicableRefactors(fileName: string, positionOrRaneg: number | TextRange, options: Options): ApplicableRefactorInfo[]; - getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string, options: Options): RefactorEditInfo | undefined; - organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, options: Options): ReadonlyArray; + getApplicableRefactors(fileName: string, positionOrRaneg: number | TextRange, options: Options | undefined): ApplicableRefactorInfo[]; + getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string, options: Options | undefined): RefactorEditInfo | undefined; + organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, options: Options | undefined): ReadonlyArray; getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean): EmitOutput; @@ -338,7 +338,12 @@ namespace ts { export type OrganizeImportsScope = CombinedCodeFixScope; /** @deprecated Use Options */ - export type GetCompletionsAtPositionOptions = Options; + export interface GetCompletionsAtPositionOptions extends Options { + /** @deprecated Use includeExternalModuleExportsInCompletionList */ + includeExternalModuleExports?: boolean; + /** @deprecated Use includeInsertTextCompletionsInCompletionList */ + includeInsertTextCompletions?: boolean; + } export interface ApplyCodeActionCommandResult { successMessage: string; diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index a90d83b44f73a..aee0df3160fa8 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -4052,8 +4052,8 @@ declare namespace ts { } interface Options { readonly quote?: "double" | "single"; - readonly includeExternalModuleExports?: boolean; - readonly includeInsertTextCompletions?: boolean; + readonly includeExternalModuleExportsInCompletionList?: boolean; + readonly includeInsertTextCompletionsInCompletionList?: boolean; } interface LanguageService { cleanupSemanticCache(): void; @@ -4070,7 +4070,7 @@ declare namespace ts { getSemanticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[]; getEncodedSyntacticClassifications(fileName: string, span: TextSpan): Classifications; getEncodedSemanticClassifications(fileName: string, span: TextSpan): Classifications; - getCompletionsAtPosition(fileName: string, position: number, settings: Options | undefined): CompletionInfo; + getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions | undefined): CompletionInfo; getCompletionEntryDetails(fileName: string, position: number, name: string, options: FormatCodeOptions | FormatCodeSettings | undefined, source: string | undefined): CompletionEntryDetails; getCompletionEntrySymbol(fileName: string, position: number, name: string, source: string | undefined): Symbol; getQuickInfoAtPosition(fileName: string, position: number): QuickInfo; @@ -4112,9 +4112,9 @@ declare namespace ts { applyCodeActionCommand(fileName: string, action: CodeActionCommand[]): Promise; /** @deprecated `fileName` will be ignored */ applyCodeActionCommand(fileName: string, action: CodeActionCommand | CodeActionCommand[]): Promise; - getApplicableRefactors(fileName: string, positionOrRaneg: number | TextRange, options: Options): ApplicableRefactorInfo[]; - getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string, options: Options): RefactorEditInfo | undefined; - organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, options: Options): ReadonlyArray; + getApplicableRefactors(fileName: string, positionOrRaneg: number | TextRange, options: Options | undefined): ApplicableRefactorInfo[]; + getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string, options: Options | undefined): RefactorEditInfo | undefined; + organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, options: Options | undefined): ReadonlyArray; getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean): EmitOutput; getProgram(): Program; dispose(): void; @@ -4125,7 +4125,12 @@ declare namespace ts { } type OrganizeImportsScope = CombinedCodeFixScope; /** @deprecated Use Options */ - type GetCompletionsAtPositionOptions = Options; + interface GetCompletionsAtPositionOptions extends Options { + /** @deprecated Use includeExternalModuleExportsInCompletionList */ + includeExternalModuleExports?: boolean; + /** @deprecated Use includeInsertTextCompletionsInCompletionList */ + includeInsertTextCompletions?: boolean; + } interface ApplyCodeActionCommandResult { successMessage: string; } @@ -4793,7 +4798,7 @@ declare namespace ts { } declare namespace ts { /** The version of the language service API */ - const servicesVersion = "0.7"; + const servicesVersion = "0.8"; function toEditorSettings(options: EditorOptions | EditorSettings): EditorSettings; function displayPartsToString(displayParts: SymbolDisplayPart[]): string; function getDefaultCompilerOptions(): CompilerOptions; @@ -7044,17 +7049,17 @@ declare namespace ts.server.protocol { insertSpaceBeforeTypeAnnotation?: boolean; } interface Options { - quote?: "double" | "single"; + readonly quote?: "double" | "single"; /** * If enabled, TypeScript will search through all external modules' exports and add them to the completions list. * This affects lone identifier completions but not completions on the right hand side of `obj.`. */ - includeExternalModuleExports?: boolean; + readonly includeExternalModuleExportsInCompletionList?: boolean; /** * If enabled, the completion list will include completions with invalid identifier names. * For those entries, The `insertText` and `replacementSpan` properties will be set to change from `.x` property access to `["x"]`. */ - includeInsertTextCompletions?: boolean; + readonly includeInsertTextCompletionsInCompletionList?: boolean; } interface CompilerOptions { allowJs?: boolean; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 51cc267a195bb..c7c760abdb07f 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -4304,8 +4304,8 @@ declare namespace ts { } interface Options { readonly quote?: "double" | "single"; - readonly includeExternalModuleExports?: boolean; - readonly includeInsertTextCompletions?: boolean; + readonly includeExternalModuleExportsInCompletionList?: boolean; + readonly includeInsertTextCompletionsInCompletionList?: boolean; } interface LanguageService { cleanupSemanticCache(): void; @@ -4322,7 +4322,7 @@ declare namespace ts { getSemanticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[]; getEncodedSyntacticClassifications(fileName: string, span: TextSpan): Classifications; getEncodedSemanticClassifications(fileName: string, span: TextSpan): Classifications; - getCompletionsAtPosition(fileName: string, position: number, settings: Options | undefined): CompletionInfo; + getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions | undefined): CompletionInfo; getCompletionEntryDetails(fileName: string, position: number, name: string, options: FormatCodeOptions | FormatCodeSettings | undefined, source: string | undefined): CompletionEntryDetails; getCompletionEntrySymbol(fileName: string, position: number, name: string, source: string | undefined): Symbol; getQuickInfoAtPosition(fileName: string, position: number): QuickInfo; @@ -4364,9 +4364,9 @@ declare namespace ts { applyCodeActionCommand(fileName: string, action: CodeActionCommand[]): Promise; /** @deprecated `fileName` will be ignored */ applyCodeActionCommand(fileName: string, action: CodeActionCommand | CodeActionCommand[]): Promise; - getApplicableRefactors(fileName: string, positionOrRaneg: number | TextRange, options: Options): ApplicableRefactorInfo[]; - getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string, options: Options): RefactorEditInfo | undefined; - organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, options: Options): ReadonlyArray; + getApplicableRefactors(fileName: string, positionOrRaneg: number | TextRange, options: Options | undefined): ApplicableRefactorInfo[]; + getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string, options: Options | undefined): RefactorEditInfo | undefined; + organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, options: Options | undefined): ReadonlyArray; getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean): EmitOutput; getProgram(): Program; dispose(): void; @@ -4377,7 +4377,12 @@ declare namespace ts { } type OrganizeImportsScope = CombinedCodeFixScope; /** @deprecated Use Options */ - type GetCompletionsAtPositionOptions = Options; + interface GetCompletionsAtPositionOptions extends Options { + /** @deprecated Use includeExternalModuleExportsInCompletionList */ + includeExternalModuleExports?: boolean; + /** @deprecated Use includeInsertTextCompletionsInCompletionList */ + includeInsertTextCompletions?: boolean; + } interface ApplyCodeActionCommandResult { successMessage: string; } @@ -5045,7 +5050,7 @@ declare namespace ts { } declare namespace ts { /** The version of the language service API */ - const servicesVersion = "0.7"; + const servicesVersion = "0.8"; function toEditorSettings(options: EditorOptions | EditorSettings): EditorSettings; function displayPartsToString(displayParts: SymbolDisplayPart[]): string; function getDefaultCompilerOptions(): CompilerOptions; From 700b7e7cd9bacbcb60c6ee975f3b66ad1f631977 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 6 Mar 2018 14:48:02 -0800 Subject: [PATCH 05/10] More renames --- src/harness/fourslash.ts | 2 +- src/server/protocol.ts | 4 ++-- src/services/completions.ts | 12 ++++++------ src/services/services.ts | 4 ++-- src/services/types.ts | 8 ++++---- tests/baselines/reference/api/tsserverlibrary.d.ts | 12 ++++++------ tests/baselines/reference/api/typescript.d.ts | 8 ++++---- 7 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 436ba364cfb5b..dead5a3305126 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -2392,7 +2392,7 @@ Actual: ${stringify(fullActual)}`); public applyCodeActionFromCompletion(markerName: string, options: FourSlashInterface.VerifyCompletionActionOptions) { this.goToMarker(markerName); - const actualCompletion = this.getCompletionListAtCaret({ ...ts.defaultOptions, includeExternalModuleExportsInCompletionList: true }).entries.find(e => + const actualCompletion = this.getCompletionListAtCaret({ ...ts.defaultOptions, includeCompletionsForExternalModuleExports: true }).entries.find(e => e.name === options.name && e.source === options.source); if (!actualCompletion.hasAction) { diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 4f9107627a554..15fc9e5f955a3 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -2594,12 +2594,12 @@ namespace ts.server.protocol { * If enabled, TypeScript will search through all external modules' exports and add them to the completions list. * This affects lone identifier completions but not completions on the right hand side of `obj.`. */ - readonly includeExternalModuleExportsInCompletionList?: boolean; + readonly includeCompletionsForExternalModuleExports?: boolean; /** * If enabled, the completion list will include completions with invalid identifier names. * For those entries, The `insertText` and `replacementSpan` properties will be set to change from `.x` property access to `["x"]`. */ - readonly includeInsertTextCompletionsInCompletionList?: boolean; + readonly includeCompletionsWithInsertText?: boolean; } export interface CompilerOptions { diff --git a/src/services/completions.ts b/src/services/completions.ts index 0c33af7619541..f82b92fe8869c 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -206,7 +206,7 @@ namespace ts.Completions { let insertText: string | undefined; let replacementSpan: TextSpan | undefined; - if (options.includeInsertTextCompletionsInCompletionList) { + if (options.includeCompletionsWithInsertText) { if (origin && origin.type === "this-type") { insertText = needsConvertPropertyAccess ? `this[${quote(name, options)}]` : `this.${name}`; } @@ -227,7 +227,7 @@ namespace ts.Completions { } } - if (insertText !== undefined && !options.includeInsertTextCompletionsInCompletionList) { + if (insertText !== undefined && !options.includeCompletionsWithInsertText) { return undefined; } @@ -479,7 +479,7 @@ namespace ts.Completions { { name, source }: CompletionEntryIdentifier, allSourceFiles: ReadonlyArray, ): SymbolCompletion | { type: "request", request: Request } | { type: "none" } { - const completionData = getCompletionData(typeChecker, log, sourceFile, position, allSourceFiles, { includeExternalModuleExportsInCompletionList: true, includeInsertTextCompletionsInCompletionList: true }, compilerOptions.target); + const completionData = getCompletionData(typeChecker, log, sourceFile, position, allSourceFiles, { includeCompletionsForExternalModuleExports: true, includeCompletionsWithInsertText: true }, compilerOptions.target); if (!completionData) { return { type: "none" }; } @@ -746,7 +746,7 @@ namespace ts.Completions { sourceFile: SourceFile, position: number, allSourceFiles: ReadonlyArray, - options: Pick, + options: Pick, target: ScriptTarget, ): CompletionData | Request | undefined { let start = timestamp(); @@ -1150,7 +1150,7 @@ namespace ts.Completions { symbols = Debug.assertEachDefined(typeChecker.getSymbolsInScope(scopeNode, symbolMeanings), "getSymbolsInScope() should all be defined"); // Need to insert 'this.' before properties of `this` type, so only do that if `includeInsertTextCompletions` - if (options.includeInsertTextCompletionsInCompletionList && scopeNode.kind !== SyntaxKind.SourceFile) { + if (options.includeCompletionsWithInsertText && scopeNode.kind !== SyntaxKind.SourceFile) { const thisType = typeChecker.tryGetThisTypeAt(scopeNode); if (thisType) { for (const symbol of getPropertiesForCompletion(thisType, typeChecker, /*isForAccess*/ true)) { @@ -1160,7 +1160,7 @@ namespace ts.Completions { } } - if (options.includeExternalModuleExportsInCompletionList) { + if (options.includeCompletionsForExternalModuleExports) { getSymbolsFromOtherSourceFileExports(symbols, previousToken && isIdentifier(previousToken) ? previousToken.text : "", target); } filterGlobalCompletion(symbols); diff --git a/src/services/services.ts b/src/services/services.ts index ac9efb99d0a58..203656f1bbd82 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1428,8 +1428,8 @@ namespace ts { // Convert from deprecated options names to new names const fullOptions: Options = { ...identity(options), // avoid excess property check - includeExternalModuleExportsInCompletionList: options.includeExternalModuleExportsInCompletionList || options.includeExternalModuleExports, - includeInsertTextCompletionsInCompletionList: options.includeInsertTextCompletionsInCompletionList || options.includeInsertTextCompletions, + includeCompletionsForExternalModuleExports: options.includeCompletionsForExternalModuleExports || options.includeExternalModuleExports, + includeCompletionsWithInsertText: options.includeCompletionsWithInsertText || options.includeInsertTextCompletions, }; synchronizeHostData(); return Completions.getCompletionsAtPosition( diff --git a/src/services/types.ts b/src/services/types.ts index 0d646d47564f6..7d7d086b9dd57 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -216,8 +216,8 @@ namespace ts { export interface Options { readonly quote?: "double" | "single"; - readonly includeExternalModuleExportsInCompletionList?: boolean; - readonly includeInsertTextCompletionsInCompletionList?: boolean; + readonly includeCompletionsForExternalModuleExports?: boolean; + readonly includeCompletionsWithInsertText?: boolean; } /* @internal */ export const defaultOptions: Options = {}; @@ -339,9 +339,9 @@ namespace ts { /** @deprecated Use Options */ export interface GetCompletionsAtPositionOptions extends Options { - /** @deprecated Use includeExternalModuleExportsInCompletionList */ + /** @deprecated Use includeCompletionsForExternalModuleExports */ includeExternalModuleExports?: boolean; - /** @deprecated Use includeInsertTextCompletionsInCompletionList */ + /** @deprecated Use includeCompletionsWithInsertText */ includeInsertTextCompletions?: boolean; } diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index aee0df3160fa8..2ead553839e57 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -4052,8 +4052,8 @@ declare namespace ts { } interface Options { readonly quote?: "double" | "single"; - readonly includeExternalModuleExportsInCompletionList?: boolean; - readonly includeInsertTextCompletionsInCompletionList?: boolean; + readonly includeCompletionsForExternalModuleExports?: boolean; + readonly includeCompletionsWithInsertText?: boolean; } interface LanguageService { cleanupSemanticCache(): void; @@ -4126,9 +4126,9 @@ declare namespace ts { type OrganizeImportsScope = CombinedCodeFixScope; /** @deprecated Use Options */ interface GetCompletionsAtPositionOptions extends Options { - /** @deprecated Use includeExternalModuleExportsInCompletionList */ + /** @deprecated Use includeCompletionsForExternalModuleExports */ includeExternalModuleExports?: boolean; - /** @deprecated Use includeInsertTextCompletionsInCompletionList */ + /** @deprecated Use includeCompletionsWithInsertText */ includeInsertTextCompletions?: boolean; } interface ApplyCodeActionCommandResult { @@ -7054,12 +7054,12 @@ declare namespace ts.server.protocol { * If enabled, TypeScript will search through all external modules' exports and add them to the completions list. * This affects lone identifier completions but not completions on the right hand side of `obj.`. */ - readonly includeExternalModuleExportsInCompletionList?: boolean; + readonly includeCompletionsForExternalModuleExports?: boolean; /** * If enabled, the completion list will include completions with invalid identifier names. * For those entries, The `insertText` and `replacementSpan` properties will be set to change from `.x` property access to `["x"]`. */ - readonly includeInsertTextCompletionsInCompletionList?: boolean; + readonly includeCompletionsWithInsertText?: boolean; } interface CompilerOptions { allowJs?: boolean; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index c7c760abdb07f..f11bfb7d96326 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -4304,8 +4304,8 @@ declare namespace ts { } interface Options { readonly quote?: "double" | "single"; - readonly includeExternalModuleExportsInCompletionList?: boolean; - readonly includeInsertTextCompletionsInCompletionList?: boolean; + readonly includeCompletionsForExternalModuleExports?: boolean; + readonly includeCompletionsWithInsertText?: boolean; } interface LanguageService { cleanupSemanticCache(): void; @@ -4378,9 +4378,9 @@ declare namespace ts { type OrganizeImportsScope = CombinedCodeFixScope; /** @deprecated Use Options */ interface GetCompletionsAtPositionOptions extends Options { - /** @deprecated Use includeExternalModuleExportsInCompletionList */ + /** @deprecated Use includeCompletionsForExternalModuleExports */ includeExternalModuleExports?: boolean; - /** @deprecated Use includeInsertTextCompletionsInCompletionList */ + /** @deprecated Use includeCompletionsWithInsertText */ includeInsertTextCompletions?: boolean; } interface ApplyCodeActionCommandResult { From 85b8abc0b86bde330d50093c85c4c2351d667fa1 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Wed, 7 Mar 2018 07:49:57 -0800 Subject: [PATCH 06/10] More renaming --- src/harness/fourslash.ts | 2 +- src/server/protocol.ts | 2 +- src/services/completions.ts | 6 +++--- src/services/services.ts | 2 +- src/services/types.ts | 4 ++-- tests/baselines/reference/api/tsserverlibrary.d.ts | 6 +++--- tests/baselines/reference/api/typescript.d.ts | 4 ++-- tests/cases/fourslash/fourslash.ts | 1 + 8 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index dead5a3305126..4a0ceeda544e0 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -2392,7 +2392,7 @@ Actual: ${stringify(fullActual)}`); public applyCodeActionFromCompletion(markerName: string, options: FourSlashInterface.VerifyCompletionActionOptions) { this.goToMarker(markerName); - const actualCompletion = this.getCompletionListAtCaret({ ...ts.defaultOptions, includeCompletionsForExternalModuleExports: true }).entries.find(e => + const actualCompletion = this.getCompletionListAtCaret({ ...ts.defaultOptions, includeCompletionsForModuleExports: true }).entries.find(e => e.name === options.name && e.source === options.source); if (!actualCompletion.hasAction) { diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 15fc9e5f955a3..8b367f4bd77d2 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -2594,7 +2594,7 @@ namespace ts.server.protocol { * If enabled, TypeScript will search through all external modules' exports and add them to the completions list. * This affects lone identifier completions but not completions on the right hand side of `obj.`. */ - readonly includeCompletionsForExternalModuleExports?: boolean; + readonly includeCompletionsForModuleExports?: boolean; /** * If enabled, the completion list will include completions with invalid identifier names. * For those entries, The `insertText` and `replacementSpan` properties will be set to change from `.x` property access to `["x"]`. diff --git a/src/services/completions.ts b/src/services/completions.ts index f82b92fe8869c..8a1b26acb258d 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -479,7 +479,7 @@ namespace ts.Completions { { name, source }: CompletionEntryIdentifier, allSourceFiles: ReadonlyArray, ): SymbolCompletion | { type: "request", request: Request } | { type: "none" } { - const completionData = getCompletionData(typeChecker, log, sourceFile, position, allSourceFiles, { includeCompletionsForExternalModuleExports: true, includeCompletionsWithInsertText: true }, compilerOptions.target); + const completionData = getCompletionData(typeChecker, log, sourceFile, position, allSourceFiles, { includeCompletionsForModuleExports: true, includeCompletionsWithInsertText: true }, compilerOptions.target); if (!completionData) { return { type: "none" }; } @@ -746,7 +746,7 @@ namespace ts.Completions { sourceFile: SourceFile, position: number, allSourceFiles: ReadonlyArray, - options: Pick, + options: Pick, target: ScriptTarget, ): CompletionData | Request | undefined { let start = timestamp(); @@ -1160,7 +1160,7 @@ namespace ts.Completions { } } - if (options.includeCompletionsForExternalModuleExports) { + if (options.includeCompletionsForModuleExports) { getSymbolsFromOtherSourceFileExports(symbols, previousToken && isIdentifier(previousToken) ? previousToken.text : "", target); } filterGlobalCompletion(symbols); diff --git a/src/services/services.ts b/src/services/services.ts index 203656f1bbd82..9c3ae91dea1f4 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1428,7 +1428,7 @@ namespace ts { // Convert from deprecated options names to new names const fullOptions: Options = { ...identity(options), // avoid excess property check - includeCompletionsForExternalModuleExports: options.includeCompletionsForExternalModuleExports || options.includeExternalModuleExports, + includeCompletionsForModuleExports: options.includeCompletionsForModuleExports || options.includeExternalModuleExports, includeCompletionsWithInsertText: options.includeCompletionsWithInsertText || options.includeInsertTextCompletions, }; synchronizeHostData(); diff --git a/src/services/types.ts b/src/services/types.ts index 7d7d086b9dd57..d58bbcc10b3c1 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -216,7 +216,7 @@ namespace ts { export interface Options { readonly quote?: "double" | "single"; - readonly includeCompletionsForExternalModuleExports?: boolean; + readonly includeCompletionsForModuleExports?: boolean; readonly includeCompletionsWithInsertText?: boolean; } /* @internal */ @@ -339,7 +339,7 @@ namespace ts { /** @deprecated Use Options */ export interface GetCompletionsAtPositionOptions extends Options { - /** @deprecated Use includeCompletionsForExternalModuleExports */ + /** @deprecated Use includeCompletionsForModuleExports */ includeExternalModuleExports?: boolean; /** @deprecated Use includeCompletionsWithInsertText */ includeInsertTextCompletions?: boolean; diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 2ead553839e57..663359b775154 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -4052,7 +4052,7 @@ declare namespace ts { } interface Options { readonly quote?: "double" | "single"; - readonly includeCompletionsForExternalModuleExports?: boolean; + readonly includeCompletionsForModuleExports?: boolean; readonly includeCompletionsWithInsertText?: boolean; } interface LanguageService { @@ -4126,7 +4126,7 @@ declare namespace ts { type OrganizeImportsScope = CombinedCodeFixScope; /** @deprecated Use Options */ interface GetCompletionsAtPositionOptions extends Options { - /** @deprecated Use includeCompletionsForExternalModuleExports */ + /** @deprecated Use includeCompletionsForModuleExports */ includeExternalModuleExports?: boolean; /** @deprecated Use includeCompletionsWithInsertText */ includeInsertTextCompletions?: boolean; @@ -7054,7 +7054,7 @@ declare namespace ts.server.protocol { * If enabled, TypeScript will search through all external modules' exports and add them to the completions list. * This affects lone identifier completions but not completions on the right hand side of `obj.`. */ - readonly includeCompletionsForExternalModuleExports?: boolean; + readonly includeCompletionsForModuleExports?: boolean; /** * If enabled, the completion list will include completions with invalid identifier names. * For those entries, The `insertText` and `replacementSpan` properties will be set to change from `.x` property access to `["x"]`. diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index f11bfb7d96326..18e5d753b5503 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -4304,7 +4304,7 @@ declare namespace ts { } interface Options { readonly quote?: "double" | "single"; - readonly includeCompletionsForExternalModuleExports?: boolean; + readonly includeCompletionsForModuleExports?: boolean; readonly includeCompletionsWithInsertText?: boolean; } interface LanguageService { @@ -4378,7 +4378,7 @@ declare namespace ts { type OrganizeImportsScope = CombinedCodeFixScope; /** @deprecated Use Options */ interface GetCompletionsAtPositionOptions extends Options { - /** @deprecated Use includeCompletionsForExternalModuleExports */ + /** @deprecated Use includeCompletionsForModuleExports */ includeExternalModuleExports?: boolean; /** @deprecated Use includeCompletionsWithInsertText */ includeInsertTextCompletions?: boolean; diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 6d81f0cb64c3b..8fccddac4824e 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -524,6 +524,7 @@ declare namespace FourSlashInterface { } interface Options { quote?: "double" | "single"; + includeCompletionsForModuleExports?: boolean; includeInsertTextCompletions?: boolean; } interface CompletionsAtOptions extends Options { From c0e2d520380dcb5715db3cf81269b074e7ba2b87 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 12 Mar 2018 09:20:03 -0700 Subject: [PATCH 07/10] Support quote style in importFixes --- src/harness/fourslash.ts | 19 ++++++------- src/harness/harnessLanguageService.ts | 4 +-- src/server/session.ts | 2 +- src/services/codefixes/importFixes.ts | 25 ++++++++++++----- src/services/completions.ts | 27 ++++++------------- src/services/services.ts | 5 ++-- src/services/shims.ts | 16 +++++------ src/services/types.ts | 3 ++- .../reference/api/tsserverlibrary.d.ts | 2 +- tests/baselines/reference/api/typescript.d.ts | 2 +- .../fourslash/completionsImport_quoteStyle.ts | 20 ++++++++++++++ tests/cases/fourslash/fourslash.ts | 3 ++- .../fourslash/importNameCodeFix_quoteStyle.ts | 16 +++++++++++ 13 files changed, 92 insertions(+), 52 deletions(-) create mode 100644 tests/cases/fourslash/completionsImport_quoteStyle.ts create mode 100644 tests/cases/fourslash/importNameCodeFix_quoteStyle.ts diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 4a0ceeda544e0..17ab958054878 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1223,8 +1223,8 @@ Actual: ${stringify(fullActual)}`); return this.languageService.getCompletionsAtPosition(this.activeFile.fileName, this.currentCaretPosition, options); } - private getCompletionEntryDetails(entryName: string, source?: string): ts.CompletionEntryDetails { - return this.languageService.getCompletionEntryDetails(this.activeFile.fileName, this.currentCaretPosition, entryName, this.formatCodeSettings, source); + private getCompletionEntryDetails(entryName: string, source?: string, options?: ts.Options): ts.CompletionEntryDetails { + return this.languageService.getCompletionEntryDetails(this.activeFile.fileName, this.currentCaretPosition, entryName, this.formatCodeSettings, source, options); } private getReferencesAtCaret() { @@ -2399,7 +2399,7 @@ Actual: ${stringify(fullActual)}`); this.raiseError(`Completion for ${options.name} does not have an associated action.`); } - const details = this.getCompletionEntryDetails(options.name, actualCompletion.source); + const details = this.getCompletionEntryDetails(options.name, actualCompletion.source, options.options); if (details.codeActions.length !== 1) { this.raiseError(`Expected one code action, got ${details.codeActions.length}`); } @@ -2512,7 +2512,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): ts.CodeFixAction[] { + private getCodeFixes(fileName: string, errorCode?: number, options: ts.Options = ts.defaultOptions): ts.CodeFixAction[] { const diagnosticsForCodeFix = this.getDiagnostics(fileName).map(diagnostic => ({ start: diagnostic.start, length: diagnostic.length, @@ -2524,7 +2524,7 @@ Actual: ${stringify(fullActual)}`); return; } - return this.languageService.getCodeFixesAtPosition(fileName, diagnostic.start, diagnostic.start + diagnostic.length, [diagnostic.code], this.formatCodeSettings, ts.defaultOptions); + return this.languageService.getCodeFixesAtPosition(fileName, diagnostic.start, diagnostic.start + diagnostic.length, [diagnostic.code], this.formatCodeSettings, options); }); } @@ -2550,7 +2550,7 @@ Actual: ${stringify(fullActual)}`); } } - public verifyImportFixAtPosition(expectedTextArray: string[], errorCode?: number) { + public verifyImportFixAtPosition(expectedTextArray: string[], errorCode: number | undefined, options: ts.Options | undefined) { const { fileName } = this.activeFile; const ranges = this.getRanges().filter(r => r.fileName === fileName); if (ranges.length !== 1) { @@ -2558,7 +2558,7 @@ Actual: ${stringify(fullActual)}`); } const range = ts.first(ranges); - const codeFixes = this.getCodeFixes(fileName, errorCode); + const codeFixes = this.getCodeFixes(fileName, errorCode, options); if (codeFixes.length === 0) { if (expectedTextArray.length !== 0) { @@ -4215,8 +4215,8 @@ namespace FourSlashInterface { this.state.applyCodeActionFromCompletion(markerName, options); } - public importFixAtPosition(expectedTextArray: string[], errorCode?: number): void { - this.state.verifyImportFixAtPosition(expectedTextArray, errorCode); + public importFixAtPosition(expectedTextArray: string[], errorCode?: number, options?: ts.Options): void { + this.state.verifyImportFixAtPosition(expectedTextArray, errorCode, options); } public navigationBar(json: any, options?: { checkSpans?: boolean }) { @@ -4663,5 +4663,6 @@ namespace FourSlashInterface { name: string; source?: string; description: string; + options?: ts.Options; } } diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index f94a07622d37d..fc8811394808c 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -420,8 +420,8 @@ namespace Harness.LanguageService { getCompletionsAtPosition(fileName: string, position: number, options: ts.Options | undefined): ts.CompletionInfo { return unwrapJSONCallResult(this.shim.getCompletionsAtPosition(fileName, position, options)); } - getCompletionEntryDetails(fileName: string, position: number, entryName: string, options: ts.FormatCodeOptions | undefined, source: string | undefined): ts.CompletionEntryDetails { - return unwrapJSONCallResult(this.shim.getCompletionEntryDetails(fileName, position, entryName, JSON.stringify(options), source)); + getCompletionEntryDetails(fileName: string, position: number, entryName: string, formatOptions: ts.FormatCodeOptions | undefined, source: string | undefined, options: ts.Options | undefined): ts.CompletionEntryDetails { + return unwrapJSONCallResult(this.shim.getCompletionEntryDetails(fileName, position, entryName, JSON.stringify(formatOptions), source, options)); } getCompletionEntrySymbol(): ts.Symbol { throw new Error("getCompletionEntrySymbol not implemented across the shim layer."); diff --git a/src/server/session.ts b/src/server/session.ts index cd51c7e2aaba1..2c869982e9fce 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1266,7 +1266,7 @@ namespace ts.server { const result = mapDefined(args.entryNames, entryName => { const { name, source } = typeof entryName === "string" ? { name: entryName, source: undefined } : entryName; - return project.getLanguageService().getCompletionEntryDetails(file, position, name, formattingOptions, source); + return project.getLanguageService().getCompletionEntryDetails(file, position, name, formattingOptions, source, this.getOptions(file)); }); return simplifiedResult ? result.map(details => ({ ...details, codeActions: map(details.codeActions, action => this.mapCodeAction(project, action)) })) diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 74a2aba76bb51..10474280539e5 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -30,6 +30,7 @@ namespace ts.codefix { compilerOptions: CompilerOptions; getCanonicalFileName: GetCanonicalFileName; cachedImportDeclarations?: ImportDeclarationMap; + options: Options; } function createCodeAction(descriptionDiagnostic: DiagnosticMessage, diagnosticArgs: string[], changes: FileTextChanges[]): CodeFixAction { @@ -53,7 +54,8 @@ namespace ts.codefix { cachedImportDeclarations: [], getCanonicalFileName: createGetCanonicalFileName(useCaseSensitiveFileNames), symbolName, - symbolToken + symbolToken, + options: context.options, }; } @@ -95,12 +97,13 @@ namespace ts.codefix { formatContext: ts.formatting.FormatContext, getCanonicalFileName: GetCanonicalFileName, symbolToken: Node | undefined, + options: Options, ): { readonly moduleSpecifier: string, readonly codeAction: CodeAction } { const exportInfos = getAllReExportingModules(exportedSymbol, checker, allSourceFiles); Debug.assert(exportInfos.some(info => info.moduleSymbol === moduleSymbol)); // We sort the best codefixes first, so taking `first` is best for completions. const moduleSpecifier = first(getNewImportInfos(program, sourceFile, exportInfos, compilerOptions, getCanonicalFileName, host)).moduleSpecifier; - const ctx: ImportCodeFixContext = { host, program, checker, compilerOptions, sourceFile, formatContext, symbolName, getCanonicalFileName, symbolToken }; + const ctx: ImportCodeFixContext = { host, program, checker, compilerOptions, sourceFile, formatContext, symbolName, getCanonicalFileName, symbolToken, options }; return { moduleSpecifier, codeAction: first(getCodeActionsForImport(exportInfos, ctx)) }; } function getAllReExportingModules(exportedSymbol: Symbol, checker: TypeChecker, allSourceFiles: ReadonlyArray): ReadonlyArray { @@ -181,12 +184,12 @@ namespace ts.codefix { } } - function getCodeActionForNewImport(context: SymbolContext, { moduleSpecifier, importKind }: NewImportInfo): CodeFixAction { + function getCodeActionForNewImport(context: SymbolContext & { options: Options }, { moduleSpecifier, importKind }: NewImportInfo): CodeFixAction { const { sourceFile, symbolName } = context; const lastImportDeclaration = findLast(sourceFile.statements, isAnyImportSyntax); const moduleSpecifierWithoutQuotes = stripQuotes(moduleSpecifier); - const quotedModuleSpecifier = createStringLiteralWithQuoteStyle(sourceFile, moduleSpecifierWithoutQuotes); + const quotedModuleSpecifier = createStringLiteralWithQuoteStyle(sourceFile, moduleSpecifierWithoutQuotes, context.options); const importDecl = importKind !== ImportKind.Equals ? createImportDeclaration( /*decorators*/ undefined, @@ -214,12 +217,20 @@ namespace ts.codefix { return createCodeAction(Diagnostics.Import_0_from_module_1, [symbolName, moduleSpecifierWithoutQuotes], changes); } - function createStringLiteralWithQuoteStyle(sourceFile: SourceFile, text: string): StringLiteral { + function createStringLiteralWithQuoteStyle(sourceFile: SourceFile, text: string, options: Options): StringLiteral { const literal = createLiteral(text); - const firstModuleSpecifier = firstOrUndefined(sourceFile.imports); - literal.singleQuote = !!firstModuleSpecifier && !isStringDoubleQuoted(firstModuleSpecifier, sourceFile); + literal.singleQuote = shouldUseSingleQuote(sourceFile, options); return literal; } + function shouldUseSingleQuote(sourceFile: SourceFile, options: Options): boolean { + if (options.quote) { + return options.quote === "single"; + } + else { + const firstModuleSpecifier = firstOrUndefined(sourceFile.imports); + return !!firstModuleSpecifier && !isStringDoubleQuoted(firstModuleSpecifier, sourceFile); + } + } function usesJsExtensionOnImports(sourceFile: SourceFile): boolean { return firstDefined(sourceFile.imports, ({ text }) => pathIsRelative(text) ? fileExtensionIs(text, Extension.Js) : undefined) || false; diff --git a/src/services/completions.ts b/src/services/completions.ts index 8a1b26acb258d..d7f836ecea7b5 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -524,6 +524,7 @@ namespace ts.Completions { host: LanguageServiceHost, formatContext: formatting.FormatContext, getCanonicalFileName: GetCanonicalFileName, + options: Options, ): CompletionEntryDetails { const typeChecker = program.getTypeChecker(); const { name } = entryId; @@ -545,7 +546,7 @@ namespace ts.Completions { } case "symbol": { const { symbol, location, symbolToOriginInfoMap, previousToken } = symbolCompletion; - const { codeActions, sourceDisplay } = getCompletionEntryCodeActionsAndSourceDisplay(symbolToOriginInfoMap, symbol, program, typeChecker, host, compilerOptions, sourceFile, previousToken, formatContext, getCanonicalFileName, allSourceFiles); + const { codeActions, sourceDisplay } = getCompletionEntryCodeActionsAndSourceDisplay(symbolToOriginInfoMap, symbol, program, typeChecker, host, compilerOptions, sourceFile, previousToken, formatContext, getCanonicalFileName, allSourceFiles, options); const kindModifiers = SymbolDisplay.getSymbolModifiers(symbol); const { displayParts, documentation, symbolKind, tags } = SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker, symbol, sourceFile, location, location, SemanticMeaning.All); return { name, kindModifiers, kind: symbolKind, displayParts, documentation, tags, codeActions, source: sourceDisplay }; @@ -585,26 +586,13 @@ namespace ts.Completions { formatContext: formatting.FormatContext, getCanonicalFileName: GetCanonicalFileName, allSourceFiles: ReadonlyArray, + options: Options, ): CodeActionsAndSourceDisplay { const symbolOriginInfo = symbolToOriginInfoMap[getSymbolId(symbol)]; - return symbolOriginInfo && symbolOriginInfo.type === "export" - ? getCodeActionsAndSourceDisplayForImport(symbolOriginInfo, symbol, program, checker, host, compilerOptions, sourceFile, previousToken, formatContext, getCanonicalFileName, allSourceFiles) - : { codeActions: undefined, sourceDisplay: undefined }; - } + if (!symbolOriginInfo || symbolOriginInfo.type !== "export") { + return { codeActions: undefined, sourceDisplay: undefined }; + } - function getCodeActionsAndSourceDisplayForImport( - symbolOriginInfo: SymbolOriginInfoExport, - symbol: Symbol, - program: Program, - checker: TypeChecker, - host: LanguageServiceHost, - compilerOptions: CompilerOptions, - sourceFile: SourceFile, - previousToken: Node, - formatContext: formatting.FormatContext, - getCanonicalFileName: GetCanonicalFileName, - allSourceFiles: ReadonlyArray - ): CodeActionsAndSourceDisplay { const { moduleSymbol } = symbolOriginInfo; const exportedSymbol = skipAlias(symbol.exportSymbol || symbol, checker); const { moduleSpecifier, codeAction } = codefix.getImportCompletionAction( @@ -619,7 +607,8 @@ namespace ts.Completions { allSourceFiles, formatContext, getCanonicalFileName, - previousToken); + previousToken, + options); return { sourceDisplay: [textPart(moduleSpecifier)], codeActions: [codeAction] }; } diff --git a/src/services/services.ts b/src/services/services.ts index 9c3ae91dea1f4..3d3176fa5aa3f 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1443,7 +1443,7 @@ namespace ts { fullOptions); } - function getCompletionEntryDetails(fileName: string, position: number, name: string, formattingOptions?: FormatCodeSettings, source?: string): CompletionEntryDetails { + function getCompletionEntryDetails(fileName: string, position: number, name: string, formattingOptions: FormatCodeSettings | undefined, source: string | undefined, options: Options = defaultOptions): CompletionEntryDetails { synchronizeHostData(); return Completions.getCompletionEntryDetails( program, @@ -1455,7 +1455,8 @@ namespace ts { program.getSourceFiles(), host, formattingOptions && formatting.getFormatContext(formattingOptions), - getCanonicalFileName); + getCanonicalFileName, + options); } function getCompletionEntrySymbol(fileName: string, position: number, name: string, source?: string): Symbol { diff --git a/src/services/shims.ts b/src/services/shims.ts index 7d6ec740dad04..c48ecbcad7ffe 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -151,8 +151,8 @@ namespace ts { getEncodedSyntacticClassifications(fileName: string, start: number, length: number): string; getEncodedSemanticClassifications(fileName: string, start: number, length: number): string; - getCompletionsAtPosition(fileName: string, position: number, settings: Options | undefined): string; - getCompletionEntryDetails(fileName: string, position: number, entryName: string, options: string/*Services.FormatCodeOptions*/ | undefined, source: string | undefined): string; + getCompletionsAtPosition(fileName: string, position: number, options: Options | undefined): string; + getCompletionEntryDetails(fileName: string, position: number, entryName: string, formatOptions: string/*Services.FormatCodeOptions*/ | undefined, source: string | undefined, options: Options | undefined): string; getQuickInfoAtPosition(fileName: string, position: number): string; @@ -909,20 +909,20 @@ namespace ts { * to provide at the given source position and providing a member completion * list if requested. */ - public getCompletionsAtPosition(fileName: string, position: number, settings: Options | undefined) { + public getCompletionsAtPosition(fileName: string, position: number, options: Options | undefined) { return this.forwardJSONCall( - `getCompletionsAtPosition('${fileName}', ${position}, ${settings})`, - () => this.languageService.getCompletionsAtPosition(fileName, position, settings) + `getCompletionsAtPosition('${fileName}', ${position}, ${options})`, + () => this.languageService.getCompletionsAtPosition(fileName, position, options) ); } /** Get a string based representation of a completion list entry details */ - public getCompletionEntryDetails(fileName: string, position: number, entryName: string, options: string/*Services.FormatCodeOptions*/ | undefined, source: string | undefined) { + public getCompletionEntryDetails(fileName: string, position: number, entryName: string, formatOptions: string/*Services.FormatCodeOptions*/ | undefined, source: string | undefined, options: Options | undefined) { return this.forwardJSONCall( `getCompletionEntryDetails('${fileName}', ${position}, '${entryName}')`, () => { - const localOptions: ts.FormatCodeOptions = options === undefined ? undefined : JSON.parse(options); - return this.languageService.getCompletionEntryDetails(fileName, position, entryName, localOptions, source); + const localOptions: ts.FormatCodeOptions = formatOptions === undefined ? undefined : JSON.parse(formatOptions); + return this.languageService.getCompletionEntryDetails(fileName, position, entryName, localOptions, source, options); } ); } diff --git a/src/services/types.ts b/src/services/types.ts index d58bbcc10b3c1..7c39caf884c11 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -256,8 +256,9 @@ namespace ts { fileName: string, position: number, name: string, - options: FormatCodeOptions | FormatCodeSettings | undefined, + formatOptions: FormatCodeOptions | FormatCodeSettings | undefined, source: string | undefined, + options: Options | undefined, ): CompletionEntryDetails; getCompletionEntrySymbol(fileName: string, position: number, name: string, source: string | undefined): Symbol; diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 663359b775154..43b8493f1829c 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -4071,7 +4071,7 @@ declare namespace ts { getEncodedSyntacticClassifications(fileName: string, span: TextSpan): Classifications; getEncodedSemanticClassifications(fileName: string, span: TextSpan): Classifications; getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions | undefined): CompletionInfo; - getCompletionEntryDetails(fileName: string, position: number, name: string, options: FormatCodeOptions | FormatCodeSettings | undefined, source: string | undefined): CompletionEntryDetails; + getCompletionEntryDetails(fileName: string, position: number, name: string, formatOptions: FormatCodeOptions | FormatCodeSettings | undefined, source: string | undefined, options: Options | undefined): CompletionEntryDetails; getCompletionEntrySymbol(fileName: string, position: number, name: string, source: string | undefined): Symbol; getQuickInfoAtPosition(fileName: string, position: number): QuickInfo; getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): TextSpan; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 18e5d753b5503..a3dd4211f24d1 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -4323,7 +4323,7 @@ declare namespace ts { getEncodedSyntacticClassifications(fileName: string, span: TextSpan): Classifications; getEncodedSemanticClassifications(fileName: string, span: TextSpan): Classifications; getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions | undefined): CompletionInfo; - getCompletionEntryDetails(fileName: string, position: number, name: string, options: FormatCodeOptions | FormatCodeSettings | undefined, source: string | undefined): CompletionEntryDetails; + getCompletionEntryDetails(fileName: string, position: number, name: string, formatOptions: FormatCodeOptions | FormatCodeSettings | undefined, source: string | undefined, options: Options | undefined): CompletionEntryDetails; getCompletionEntrySymbol(fileName: string, position: number, name: string, source: string | undefined): Symbol; getQuickInfoAtPosition(fileName: string, position: number): QuickInfo; getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): TextSpan; diff --git a/tests/cases/fourslash/completionsImport_quoteStyle.ts b/tests/cases/fourslash/completionsImport_quoteStyle.ts new file mode 100644 index 0000000000000..86031a6aff892 --- /dev/null +++ b/tests/cases/fourslash/completionsImport_quoteStyle.ts @@ -0,0 +1,20 @@ +/// + +// @Filename: /a.ts +////export const foo = 0; + +// @Filename: /b.ts +////fo/**/ + +goTo.marker(""); +verify.applyCodeActionFromCompletion("", { + name: "foo", + source: "/a", + description: `Import 'foo' from module "./a"`, + options: { + quote: "single", + }, + newFileContent: `import { foo } from './a'; + +fo`, +}); diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 8fccddac4824e..2ebaac14bc84d 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -209,6 +209,7 @@ declare namespace FourSlashInterface { description: string, newFileContent?: string, newRangeContent?: string, + options?: Options, }); indentationIs(numberOfSpaces: number): void; indentationAtPositionIs(fileName: string, position: number, numberOfSpaces: number, indentStyle?: ts.IndentStyle, baseIndentSize?: number): void; @@ -296,7 +297,7 @@ declare namespace FourSlashInterface { rangeIs(expectedText: string, includeWhiteSpace?: boolean): void; fileAfterApplyingRefactorAtMarker(markerName: string, expectedContent: string, refactorNameToApply: string, formattingOptions?: FormatCodeOptions): void; getAndApplyCodeFix(errorCode?: number, index?: number): void; - importFixAtPosition(expectedTextArray: string[], errorCode?: number): void; + importFixAtPosition(expectedTextArray: string[], errorCode?: number, options?: Options): void; navigationBar(json: any, options?: { checkSpans?: boolean }): void; navigationTree(json: any, options?: { checkSpans?: boolean }): void; diff --git a/tests/cases/fourslash/importNameCodeFix_quoteStyle.ts b/tests/cases/fourslash/importNameCodeFix_quoteStyle.ts new file mode 100644 index 0000000000000..3e16a39252993 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFix_quoteStyle.ts @@ -0,0 +1,16 @@ +/// + +// @Filename: /a.ts +////export const foo: number; + +// @Filename: /b.ts +////[|foo;|] + +goTo.file("/b.ts"); +verify.importFixAtPosition([ +`import { foo } from './a'; + +foo;`, +], /*errorCode*/ undefined, { + quote: "single", +}); From e8033dcd4c20293532f0aaf758ce92de6bf940df Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 12 Mar 2018 14:41:32 -0700 Subject: [PATCH 08/10] Add `importModuleSpecifierPreference` option --- src/services/codefixes/importFixes.ts | 25 ++++++++++++------- src/services/types.ts | 1 + tests/cases/fourslash/fourslash.ts | 1 + .../importNameCodeFixNewImportBaseUrl1.ts | 8 ++++++ .../importNameCodeFixNewImportBaseUrl2.ts | 9 +++++++ 5 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 10474280539e5..758f2cefa9fd0 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -102,7 +102,7 @@ namespace ts.codefix { const exportInfos = getAllReExportingModules(exportedSymbol, checker, allSourceFiles); Debug.assert(exportInfos.some(info => info.moduleSymbol === moduleSymbol)); // We sort the best codefixes first, so taking `first` is best for completions. - const moduleSpecifier = first(getNewImportInfos(program, sourceFile, exportInfos, compilerOptions, getCanonicalFileName, host)).moduleSpecifier; + const moduleSpecifier = first(getNewImportInfos(program, sourceFile, exportInfos, compilerOptions, getCanonicalFileName, host, options)).moduleSpecifier; const ctx: ImportCodeFixContext = { host, program, checker, compilerOptions, sourceFile, formatContext, symbolName, getCanonicalFileName, symbolToken, options }; return { moduleSpecifier, codeAction: first(getCodeActionsForImport(exportInfos, ctx)) }; } @@ -254,25 +254,26 @@ namespace ts.codefix { program: Program, sourceFile: SourceFile, moduleSymbols: ReadonlyArray, - options: CompilerOptions, + compilerOptions: CompilerOptions, getCanonicalFileName: (file: string) => string, host: LanguageServiceHost, + options: Options, ): ReadonlyArray { - const { baseUrl, paths, rootDirs } = options; + const { baseUrl, paths, rootDirs } = compilerOptions; const addJsExtension = usesJsExtensionOnImports(sourceFile); const choicesForEachExportingModule = flatMap(moduleSymbols, ({ moduleSymbol, importKind }) => { const modulePathsGroups = getAllModulePaths(program, moduleSymbol.valueDeclaration.getSourceFile()).map(moduleFileName => { const sourceDirectory = getDirectoryPath(sourceFile.fileName); const global = tryGetModuleNameFromAmbientModule(moduleSymbol) - || tryGetModuleNameFromTypeRoots(options, host, getCanonicalFileName, moduleFileName, addJsExtension) - || tryGetModuleNameAsNodeModule(options, moduleFileName, host, getCanonicalFileName, sourceDirectory) + || tryGetModuleNameFromTypeRoots(compilerOptions, host, getCanonicalFileName, moduleFileName, addJsExtension) + || tryGetModuleNameAsNodeModule(compilerOptions, moduleFileName, host, getCanonicalFileName, sourceDirectory) || rootDirs && tryGetModuleNameFromRootDirs(rootDirs, moduleFileName, sourceDirectory, getCanonicalFileName); if (global) { return [global]; } - const relativePath = removeExtensionAndIndexPostFix(getRelativePath(moduleFileName, sourceDirectory, getCanonicalFileName), options, addJsExtension); - if (!baseUrl) { + const relativePath = removeExtensionAndIndexPostFix(getRelativePath(moduleFileName, sourceDirectory, getCanonicalFileName), compilerOptions, addJsExtension); + if (!baseUrl || options.importModuleSpecifierPreference == "relative") { return [relativePath]; } @@ -281,7 +282,7 @@ namespace ts.codefix { return [relativePath]; } - const importRelativeToBaseUrl = removeExtensionAndIndexPostFix(relativeToBaseUrl, options, addJsExtension); + const importRelativeToBaseUrl = removeExtensionAndIndexPostFix(relativeToBaseUrl, compilerOptions, addJsExtension); if (paths) { const fromPaths = tryGetModuleNameFromPaths(removeFileExtension(relativeToBaseUrl), importRelativeToBaseUrl, paths); if (fromPaths) { @@ -289,6 +290,12 @@ namespace ts.codefix { } } + if (options.importModuleSpecifierPreference === "baseUrl") { + return [importRelativeToBaseUrl]; + } + + if (options.importModuleSpecifierPreference !== undefined) Debug.assertNever(options.importModuleSpecifierPreference); + if (isPathRelativeToParent(relativeToBaseUrl)) { return [relativePath]; } @@ -584,7 +591,7 @@ namespace ts.codefix { const existingDeclaration = firstDefined(existingImports, newImportInfoFromExistingSpecifier); const newImportInfos = existingDeclaration ? [existingDeclaration] - : getNewImportInfos(ctx.program, ctx.sourceFile, exportInfos, ctx.compilerOptions, ctx.getCanonicalFileName, ctx.host); + : getNewImportInfos(ctx.program, ctx.sourceFile, exportInfos, ctx.compilerOptions, ctx.getCanonicalFileName, ctx.host, ctx.options); return newImportInfos.map(info => getCodeActionForNewImport(ctx, info)); } diff --git a/src/services/types.ts b/src/services/types.ts index 7c39caf884c11..1bb6b8679bc55 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -218,6 +218,7 @@ namespace ts { readonly quote?: "double" | "single"; readonly includeCompletionsForModuleExports?: boolean; readonly includeCompletionsWithInsertText?: boolean; + readonly importModuleSpecifierPreference?: "relative" | "baseUrl"; } /* @internal */ export const defaultOptions: Options = {}; diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 2ebaac14bc84d..a367ba1739acd 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -527,6 +527,7 @@ declare namespace FourSlashInterface { quote?: "double" | "single"; includeCompletionsForModuleExports?: boolean; includeInsertTextCompletions?: boolean; + importModuleSpecifierPreference?: "relative" | "baseUrl"; } interface CompletionsAtOptions extends Options { isNewIdentifierLocation?: boolean; diff --git a/tests/cases/fourslash/importNameCodeFixNewImportBaseUrl1.ts b/tests/cases/fourslash/importNameCodeFixNewImportBaseUrl1.ts index e690cfd3dbbf8..d34193b9a6082 100644 --- a/tests/cases/fourslash/importNameCodeFixNewImportBaseUrl1.ts +++ b/tests/cases/fourslash/importNameCodeFixNewImportBaseUrl1.ts @@ -23,3 +23,11 @@ f1();`, f1();` ]); + +verify.importFixAtPosition([ +`import { f1 } from "b/x"; + +f1();`, +], /*errorCode*/ undefined, { + importModuleSpecifierPreference: "baseUrl", +}); diff --git a/tests/cases/fourslash/importNameCodeFixNewImportBaseUrl2.ts b/tests/cases/fourslash/importNameCodeFixNewImportBaseUrl2.ts index 658bd25823feb..8fc9a97afe344 100644 --- a/tests/cases/fourslash/importNameCodeFixNewImportBaseUrl2.ts +++ b/tests/cases/fourslash/importNameCodeFixNewImportBaseUrl2.ts @@ -23,3 +23,12 @@ f1();`, f1();` ]); + +verify.importFixAtPosition([ +`import { f1 } from "../b/x"; + +f1();`, +], /*errorCode*/ undefined, { + importModuleSpecifierPreference: "relative", +}); + From ca0beaf680ad45488de2ca705dff064b9c00bea2 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 12 Mar 2018 15:20:53 -0700 Subject: [PATCH 09/10] Support quote style for `throw new Error('Method not implemented.')` (#18169) --- src/compiler/factory.ts | 7 ++- src/harness/fourslash.ts | 3 +- src/services/codefixes/fixAddMissingMember.ts | 32 +++++++++++--- ...sDoesntImplementInheritedAbstractMember.ts | 8 ++-- .../fixClassIncorrectlyImplementsInterface.ts | 9 ++-- src/services/codefixes/helpers.ts | 43 +++++++++++++------ src/services/codefixes/importFixes.ts | 9 +--- ...eFixClassImplementInterface_optionQuote.ts | 20 +++++++++ tests/cases/fourslash/fourslash.ts | 1 + 9 files changed, 93 insertions(+), 39 deletions(-) create mode 100644 tests/cases/fourslash/codeFixClassImplementInterface_optionQuote.ts diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 56d132fc925b4..2c91176ec10f0 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -70,12 +70,13 @@ namespace ts { // Literals + /* @internal */ export function createLiteral(value: string | StringLiteral | NoSubstitutionTemplateLiteral | NumericLiteral | Identifier, isSingleQuote: boolean): StringLiteral; // tslint:disable-line unified-signatures /** If a node is passed, creates a string literal whose source text is read from a source node during emit. */ export function createLiteral(value: string | StringLiteral | NoSubstitutionTemplateLiteral | NumericLiteral | Identifier): StringLiteral; export function createLiteral(value: number): NumericLiteral; export function createLiteral(value: boolean): BooleanLiteral; export function createLiteral(value: string | number | boolean): PrimaryExpression; - export function createLiteral(value: string | number | boolean | StringLiteral | NoSubstitutionTemplateLiteral | NumericLiteral | Identifier): PrimaryExpression { + export function createLiteral(value: string | number | boolean | StringLiteral | NoSubstitutionTemplateLiteral | NumericLiteral | Identifier, isSingleQuote?: boolean): PrimaryExpression { if (typeof value === "number") { return createNumericLiteral(value + ""); } @@ -83,7 +84,9 @@ namespace ts { return value ? createTrue() : createFalse(); } if (isString(value)) { - return createStringLiteral(value); + const res = createStringLiteral(value); + if (isSingleQuote) res.singleQuote = true; + return res; } return createLiteralFromNode(value); } diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 876e6c9b36039..fd94323b6dc94 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -2482,7 +2482,7 @@ Actual: ${stringify(fullActual)}`); public verifyCodeFix(options: FourSlashInterface.VerifyCodeFixOptions) { const fileName = this.activeFile.fileName; - const actions = this.getCodeFixes(fileName, options.errorCode); + const actions = this.getCodeFixes(fileName, options.errorCode, options.options); let index = options.index; if (index === undefined) { if (!(actions && actions.length === 1)) { @@ -4662,6 +4662,7 @@ namespace FourSlashInterface { description: string; errorCode?: number; index?: number; + options?: ts.Options; } export interface VerifyCodeFixAvailableOptions { diff --git a/src/services/codefixes/fixAddMissingMember.ts b/src/services/codefixes/fixAddMissingMember.ts index 7dbbc39843fcb..33761fef3dcf8 100644 --- a/src/services/codefixes/fixAddMissingMember.ts +++ b/src/services/codefixes/fixAddMissingMember.ts @@ -11,7 +11,7 @@ namespace ts.codefix { const info = getInfo(context.sourceFile, context.span.start, context.program.getTypeChecker()); if (!info) return undefined; const { classDeclaration, classDeclarationSourceFile, inJs, makeStatic, token, call } = info; - const methodCodeAction = call && getActionForMethodDeclaration(context, classDeclarationSourceFile, classDeclaration, token, call, makeStatic, inJs); + const methodCodeAction = call && getActionForMethodDeclaration(context, classDeclarationSourceFile, classDeclaration, token, call, makeStatic, inJs, context.options); const addMember = inJs ? singleElementArray(getActionsForAddMissingMemberInJavaScriptFile(context, classDeclarationSourceFile, classDeclaration, token.text, makeStatic)) : getActionsForAddMissingMemberInTypeScriptFile(context, classDeclarationSourceFile, classDeclaration, token, makeStatic); @@ -21,7 +21,7 @@ namespace ts.codefix { getAllCodeActions: context => { const seenNames = createMap(); return codeFixAll(context, errorCodes, (changes, diag) => { - const { program } = context; + const { program, options } = context; const info = getInfo(diag.file!, diag.start!, program.getTypeChecker()); if (!info) return; const { classDeclaration, classDeclarationSourceFile, inJs, makeStatic, token, call } = info; @@ -31,7 +31,7 @@ namespace ts.codefix { // Always prefer to add a method declaration if possible. if (call) { - addMethodDeclaration(changes, classDeclarationSourceFile, classDeclaration, token, call, makeStatic, inJs); + addMethodDeclaration(changes, classDeclarationSourceFile, classDeclaration, token, call, makeStatic, inJs, options); } else { if (inJs) { @@ -181,14 +181,32 @@ namespace ts.codefix { return { description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_index_signature_for_property_0), [tokenName]), changes, fixId: undefined }; } - function getActionForMethodDeclaration(context: CodeFixContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, token: Identifier, callExpression: CallExpression, makeStatic: boolean, inJs: boolean): CodeFixAction | undefined { + function getActionForMethodDeclaration( + context: CodeFixContext, + classDeclarationSourceFile: SourceFile, + classDeclaration: ClassLikeDeclaration, + token: Identifier, + callExpression: CallExpression, + makeStatic: boolean, + inJs: boolean, + options: Options, + ): CodeFixAction | undefined { const description = formatStringFromArgs(getLocaleSpecificMessage(makeStatic ? Diagnostics.Declare_static_method_0 : Diagnostics.Declare_method_0), [token.text]); - const changes = textChanges.ChangeTracker.with(context, t => addMethodDeclaration(t, classDeclarationSourceFile, classDeclaration, token, callExpression, makeStatic, inJs)); + const changes = textChanges.ChangeTracker.with(context, t => addMethodDeclaration(t, classDeclarationSourceFile, classDeclaration, token, callExpression, makeStatic, inJs, options)); return { description, changes, fixId }; } - function addMethodDeclaration(changeTracker: textChanges.ChangeTracker, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, token: Identifier, callExpression: CallExpression, makeStatic: boolean, inJs: boolean) { - const methodDeclaration = createMethodFromCallExpression(callExpression, token.text, inJs, makeStatic); + function addMethodDeclaration( + changeTracker: textChanges.ChangeTracker, + classDeclarationSourceFile: SourceFile, + classDeclaration: ClassLikeDeclaration, + token: Identifier, + callExpression: CallExpression, + makeStatic: boolean, + inJs: boolean, + options: Options, + ): void { + const methodDeclaration = createMethodFromCallExpression(callExpression, token.text, inJs, makeStatic, options); changeTracker.insertNodeAtClassStart(classDeclarationSourceFile, classDeclaration, methodDeclaration); } } diff --git a/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts b/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts index 786d61d798d80..f0ed745c5bdd4 100644 --- a/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts +++ b/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts @@ -10,7 +10,7 @@ namespace ts.codefix { getCodeActions(context) { const { program, sourceFile, span } = context; const changes = textChanges.ChangeTracker.with(context, t => - addMissingMembers(getClass(sourceFile, span.start), sourceFile, program.getTypeChecker(), t)); + addMissingMembers(getClass(sourceFile, span.start), sourceFile, program.getTypeChecker(), t, context.options)); return changes.length === 0 ? undefined : [{ description: getLocaleSpecificMessage(Diagnostics.Implement_inherited_abstract_class), changes, fixId }]; }, fixIds: [fixId], @@ -19,7 +19,7 @@ namespace ts.codefix { return codeFixAll(context, errorCodes, (changes, diag) => { const classDeclaration = getClass(diag.file!, diag.start!); if (addToSeen(seenClassDeclarations, getNodeId(classDeclaration))) { - addMissingMembers(classDeclaration, context.sourceFile, context.program.getTypeChecker(), changes); + addMissingMembers(classDeclaration, context.sourceFile, context.program.getTypeChecker(), changes, context.options); } }); }, @@ -32,7 +32,7 @@ namespace ts.codefix { return cast(token.parent, isClassLike); } - function addMissingMembers(classDeclaration: ClassLikeDeclaration, sourceFile: SourceFile, checker: TypeChecker, changeTracker: textChanges.ChangeTracker): void { + function addMissingMembers(classDeclaration: ClassLikeDeclaration, sourceFile: SourceFile, checker: TypeChecker, changeTracker: textChanges.ChangeTracker, options: Options): void { const extendsNode = getClassExtendsHeritageClauseElement(classDeclaration); const instantiatedExtendsType = checker.getTypeAtLocation(extendsNode); @@ -40,7 +40,7 @@ namespace ts.codefix { // so duplicates cannot occur. const abstractAndNonPrivateExtendsSymbols = checker.getPropertiesOfType(instantiatedExtendsType).filter(symbolPointsToNonPrivateAndAbstractMember); - createMissingMemberNodes(classDeclaration, abstractAndNonPrivateExtendsSymbols, checker, member => changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, member)); + createMissingMemberNodes(classDeclaration, abstractAndNonPrivateExtendsSymbols, checker, options, member => changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, member)); } function symbolPointsToNonPrivateAndAbstractMember(symbol: Symbol): boolean { diff --git a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts index c295d4fb3c73f..697e845ea5698 100644 --- a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts +++ b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts @@ -10,7 +10,7 @@ namespace ts.codefix { const classDeclaration = getClass(sourceFile, span.start); const checker = program.getTypeChecker(); return mapDefined(getClassImplementsHeritageClauseElements(classDeclaration), implementedTypeNode => { - const changes = textChanges.ChangeTracker.with(context, t => addMissingDeclarations(checker, implementedTypeNode, sourceFile, classDeclaration, t)); + const changes = textChanges.ChangeTracker.with(context, t => addMissingDeclarations(checker, implementedTypeNode, sourceFile, classDeclaration, t, context.options)); if (changes.length === 0) return undefined; const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Implement_interface_0), [implementedTypeNode.getText()]); return { description, changes, fixId }; @@ -23,7 +23,7 @@ namespace ts.codefix { const classDeclaration = getClass(diag.file!, diag.start!); if (addToSeen(seenClassDeclarations, getNodeId(classDeclaration))) { for (const implementedTypeNode of getClassImplementsHeritageClauseElements(classDeclaration)) { - addMissingDeclarations(context.program.getTypeChecker(), implementedTypeNode, diag.file!, classDeclaration, changes); + addMissingDeclarations(context.program.getTypeChecker(), implementedTypeNode, diag.file!, classDeclaration, changes, context.options); } } }); @@ -39,7 +39,8 @@ namespace ts.codefix { implementedTypeNode: ExpressionWithTypeArguments, sourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, - changeTracker: textChanges.ChangeTracker + changeTracker: textChanges.ChangeTracker, + options: Options, ): void { // Note that this is ultimately derived from a map indexed by symbol names, // so duplicates cannot occur. @@ -56,7 +57,7 @@ namespace ts.codefix { createMissingIndexSignatureDeclaration(implementedType, IndexKind.String); } - createMissingMemberNodes(classDeclaration, nonPrivateMembers, checker, member => changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, member)); + createMissingMemberNodes(classDeclaration, nonPrivateMembers, checker, options, member => changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, member)); function createMissingIndexSignatureDeclaration(type: InterfaceType, kind: IndexKind): void { const indexInfoOfKind = checker.getIndexInfoOfType(type, kind); diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts index 6a45c66ca816d..8fc94b733870d 100644 --- a/src/services/codefixes/helpers.ts +++ b/src/services/codefixes/helpers.ts @@ -6,11 +6,11 @@ namespace ts.codefix { * @param possiblyMissingSymbols The collection of symbols to filter and then get insertions for. * @returns Empty string iff there are no member insertions. */ - export function createMissingMemberNodes(classDeclaration: ClassLikeDeclaration, possiblyMissingSymbols: ReadonlyArray, checker: TypeChecker, out: (node: ClassElement) => void): void { + export function createMissingMemberNodes(classDeclaration: ClassLikeDeclaration, possiblyMissingSymbols: ReadonlyArray, checker: TypeChecker, options: Options, out: (node: ClassElement) => void): void { const classMembers = classDeclaration.symbol.members; for (const symbol of possiblyMissingSymbols) { if (!classMembers.has(symbol.escapedName)) { - addNewNodeForMemberSymbol(symbol, classDeclaration, checker, out); + addNewNodeForMemberSymbol(symbol, classDeclaration, checker, options, out); } } } @@ -18,7 +18,7 @@ namespace ts.codefix { /** * @returns Empty string iff there we can't figure out a representation for `symbol` in `enclosingDeclaration`. */ - function addNewNodeForMemberSymbol(symbol: Symbol, enclosingDeclaration: ClassLikeDeclaration, checker: TypeChecker, out: (node: Node) => void): void { + function addNewNodeForMemberSymbol(symbol: Symbol, enclosingDeclaration: ClassLikeDeclaration, checker: TypeChecker, options: Options, out: (node: Node) => void): void { const declarations = symbol.getDeclarations(); if (!(declarations && declarations.length)) { return undefined; @@ -63,7 +63,7 @@ namespace ts.codefix { if (declarations.length === 1) { Debug.assert(signatures.length === 1); const signature = signatures[0]; - outputMethod(signature, modifiers, name, createStubbedMethodBody()); + outputMethod(signature, modifiers, name, createStubbedMethodBody(options)); break; } @@ -74,11 +74,11 @@ namespace ts.codefix { if (declarations.length > signatures.length) { const signature = checker.getSignatureFromDeclaration(declarations[declarations.length - 1] as SignatureDeclaration); - outputMethod(signature, modifiers, name, createStubbedMethodBody()); + outputMethod(signature, modifiers, name, createStubbedMethodBody(options)); } else { Debug.assert(declarations.length === signatures.length); - out(createMethodImplementingSignatures(signatures, name, optional, modifiers)); + out(createMethodImplementingSignatures(signatures, name, optional, modifiers, options)); } break; } @@ -107,7 +107,13 @@ namespace ts.codefix { return nodes && createNodeArray(nodes.map(getSynthesizedDeepClone)); } - export function createMethodFromCallExpression({ typeArguments, arguments: args }: CallExpression, methodName: string, inJs: boolean, makeStatic: boolean): MethodDeclaration { + export function createMethodFromCallExpression( + { typeArguments, arguments: args }: CallExpression, + methodName: string, + inJs: boolean, + makeStatic: boolean, + options: Options, + ): MethodDeclaration { return createMethod( /*decorators*/ undefined, /*modifiers*/ makeStatic ? [createToken(SyntaxKind.StaticKeyword)] : undefined, @@ -118,7 +124,7 @@ namespace ts.codefix { createTypeParameterDeclaration(CharacterCodes.T + typeArguments.length - 1 <= CharacterCodes.Z ? String.fromCharCode(CharacterCodes.T + i) : `T${i}`)), /*parameters*/ createDummyParameters(args.length, /*names*/ undefined, /*minArgumentCount*/ undefined, inJs), /*type*/ inJs ? undefined : createKeywordTypeNode(SyntaxKind.AnyKeyword), - createStubbedMethodBody()); + createStubbedMethodBody(options)); } function createDummyParameters(argCount: number, names: string[] | undefined, minArgumentCount: number | undefined, inJs: boolean): ParameterDeclaration[] { @@ -137,7 +143,13 @@ namespace ts.codefix { return parameters; } - function createMethodImplementingSignatures(signatures: ReadonlyArray, name: PropertyName, optional: boolean, modifiers: ReadonlyArray | undefined): MethodDeclaration { + function createMethodImplementingSignatures( + signatures: ReadonlyArray, + name: PropertyName, + optional: boolean, + modifiers: ReadonlyArray | undefined, + options: Options, + ): MethodDeclaration { /** This is *a* signature with the maximal number of arguments, * such that if there is a "maximal" signature without rest arguments, * this is one of them. @@ -178,7 +190,8 @@ namespace ts.codefix { optional, /*typeParameters*/ undefined, parameters, - /*returnType*/ undefined); + /*returnType*/ undefined, + options); } function createStubbedMethod( @@ -187,7 +200,9 @@ namespace ts.codefix { optional: boolean, typeParameters: ReadonlyArray | undefined, parameters: ReadonlyArray, - returnType: TypeNode | undefined) { + returnType: TypeNode | undefined, + options: Options + ): MethodDeclaration { return createMethod( /*decorators*/ undefined, modifiers, @@ -197,16 +212,16 @@ namespace ts.codefix { typeParameters, parameters, returnType, - createStubbedMethodBody()); + createStubbedMethodBody(options)); } - function createStubbedMethodBody() { + function createStubbedMethodBody(options: Options): Block { return createBlock( [createThrow( createNew( createIdentifier("Error"), /*typeArguments*/ undefined, - [createLiteral("Method not implemented.")]))], + [createLiteral("Method not implemented.", /*isSingleQuote*/ options.quote === "single")]))], /*multiline*/ true); } diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 78f92b5c37be4..83cc7f91c5a4b 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -185,11 +185,11 @@ namespace ts.codefix { } function getCodeActionForNewImport(context: SymbolContext & { options: Options }, { moduleSpecifier, importKind }: NewImportInfo): CodeFixAction { - const { sourceFile, symbolName } = context; + const { sourceFile, symbolName, options } = context; const lastImportDeclaration = findLast(sourceFile.statements, isAnyImportSyntax); const moduleSpecifierWithoutQuotes = stripQuotes(moduleSpecifier); - const quotedModuleSpecifier = createStringLiteralWithQuoteStyle(sourceFile, moduleSpecifierWithoutQuotes, context.options); + const quotedModuleSpecifier = createLiteral(moduleSpecifierWithoutQuotes, shouldUseSingleQuote(sourceFile, options)); const importDecl = importKind !== ImportKind.Equals ? createImportDeclaration( /*decorators*/ undefined, @@ -217,11 +217,6 @@ namespace ts.codefix { return createCodeAction(Diagnostics.Import_0_from_module_1, [symbolName, moduleSpecifierWithoutQuotes], changes); } - function createStringLiteralWithQuoteStyle(sourceFile: SourceFile, text: string, options: Options): StringLiteral { - const literal = createLiteral(text); - literal.singleQuote = shouldUseSingleQuote(sourceFile, options); - return literal; - } function shouldUseSingleQuote(sourceFile: SourceFile, options: Options): boolean { if (options.quote) { return options.quote === "single"; diff --git a/tests/cases/fourslash/codeFixClassImplementInterface_optionQuote.ts b/tests/cases/fourslash/codeFixClassImplementInterface_optionQuote.ts new file mode 100644 index 0000000000000..b480bb667299f --- /dev/null +++ b/tests/cases/fourslash/codeFixClassImplementInterface_optionQuote.ts @@ -0,0 +1,20 @@ +/// + +////interface I { +//// m(): void; +////} +////class C implements I {} + +verify.codeFix({ + description: "Implement interface 'I'", + newFileContent: +`interface I { + m(): void; +} +class C implements I { + m(): void { + throw new Error('Method not implemented.'); + } +}`, + options: { quote: "single" } +}); diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 7c5316b6e742e..9f7add262a7de 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -179,6 +179,7 @@ declare namespace FourSlashInterface { newRangeContent?: string, errorCode?: number, index?: number, + options?: Options, }); codeFixAvailable(options?: Array<{ description: string, actions?: Array<{ type: string, data: {} }>, commands?: {}[] }>): void; applicableRefactorAvailableAtMarker(markerName: string): void; From 6e1adafacad7f5f44f59f9fad887cbcb811da1be Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 15 Mar 2018 12:19:19 -0700 Subject: [PATCH 10/10] options -> preferences --- src/harness/fourslash.ts | 56 +++++++++---------- src/harness/harnessLanguageService.ts | 8 +-- src/harness/unittests/extractTestHelpers.ts | 4 +- src/harness/unittests/organizeImports.ts | 4 +- .../unittests/tsserverProjectSystem.ts | 12 ++-- src/server/client.ts | 4 +- src/server/editorServices.ts | 14 ++--- src/server/protocol.ts | 12 ++-- src/server/scriptInfo.ts | 14 ++--- src/server/session.ts | 18 +++--- src/services/codeFixProvider.ts | 2 +- src/services/codefixes/fixAddMissingMember.ts | 14 ++--- ...sDoesntImplementInheritedAbstractMember.ts | 8 +-- .../fixClassIncorrectlyImplementsInterface.ts | 8 +-- src/services/codefixes/helpers.ts | 28 +++++----- src/services/codefixes/importFixes.ts | 32 +++++------ src/services/completions.ts | 52 ++++++++--------- src/services/organizeImports.ts | 2 +- src/services/refactorProvider.ts | 2 +- src/services/services.ts | 36 ++++++------ src/services/shims.ts | 14 ++--- src/services/types.ts | 24 ++++---- .../reference/api/tsserverlibrary.d.ts | 46 +++++++-------- tests/baselines/reference/api/typescript.d.ts | 22 ++++---- ...eFixClassImplementInterface_optionQuote.ts | 2 +- ...etionListInvalidMemberNames_escapeQuote.ts | 2 +- .../fourslash/completionsImport_quoteStyle.ts | 4 +- tests/cases/fourslash/fourslash.ts | 16 +++--- .../importNameCodeFixNewImportBaseUrl1.ts | 2 +- .../fourslash/importNameCodeFix_quoteStyle.ts | 2 +- 30 files changed, 232 insertions(+), 232 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index fd94323b6dc94..b32a8bb0151aa 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1227,8 +1227,8 @@ Actual: ${stringify(fullActual)}`); return this.languageService.getCompletionsAtPosition(this.activeFile.fileName, this.currentCaretPosition, options); } - private getCompletionEntryDetails(entryName: string, source?: string, options?: ts.Options): ts.CompletionEntryDetails { - return this.languageService.getCompletionEntryDetails(this.activeFile.fileName, this.currentCaretPosition, entryName, this.formatCodeSettings, source, options); + private getCompletionEntryDetails(entryName: string, source?: string, preferences?: ts.UserPreferences): ts.CompletionEntryDetails { + return this.languageService.getCompletionEntryDetails(this.activeFile.fileName, this.currentCaretPosition, entryName, this.formatCodeSettings, source, preferences); } private getReferencesAtCaret() { @@ -1727,8 +1727,8 @@ Actual: ${stringify(fullActual)}`); Harness.IO.log(stringify(sigHelp)); } - public printCompletionListMembers(options: ts.Options | undefined) { - const completions = this.getCompletionListAtCaret(options); + public printCompletionListMembers(preferences: ts.UserPreferences | undefined) { + const completions = this.getCompletionListAtCaret(preferences); this.printMembersOrCompletions(completions); } @@ -1826,7 +1826,7 @@ Actual: ${stringify(fullActual)}`); } else if (prevChar === " " && /A-Za-z_/.test(ch)) { /* Completions */ - this.languageService.getCompletionsAtPosition(this.activeFile.fileName, offset, ts.defaultOptions); + this.languageService.getCompletionsAtPosition(this.activeFile.fileName, offset, ts.defaultPreferences); } if (i % checkCadence === 0) { @@ -2401,14 +2401,14 @@ Actual: ${stringify(fullActual)}`); public applyCodeActionFromCompletion(markerName: string, options: FourSlashInterface.VerifyCompletionActionOptions) { this.goToMarker(markerName); - const actualCompletion = this.getCompletionListAtCaret({ ...ts.defaultOptions, includeCompletionsForModuleExports: true }).entries.find(e => + const actualCompletion = this.getCompletionListAtCaret({ ...ts.defaultPreferences, includeCompletionsForModuleExports: true }).entries.find(e => e.name === options.name && e.source === options.source); if (!actualCompletion.hasAction) { this.raiseError(`Completion for ${options.name} does not have an associated action.`); } - const details = this.getCompletionEntryDetails(options.name, actualCompletion.source, options.options); + const details = this.getCompletionEntryDetails(options.name, actualCompletion.source, options.preferences); if (details.codeActions.length !== 1) { this.raiseError(`Expected one code action, got ${details.codeActions.length}`); } @@ -2453,7 +2453,7 @@ Actual: ${stringify(fullActual)}`); const { fixId, newFileContent } = options; const fixIds = ts.mapDefined(this.getCodeFixes(this.activeFile.fileName), a => a.fixId); ts.Debug.assert(ts.contains(fixIds, fixId), "No available code fix has that group id.", () => `Expected '${fixId}'. Available action ids: ${fixIds}`); - const { changes, commands } = this.languageService.getCombinedCodeFix({ type: "file", fileName: this.activeFile.fileName }, fixId, this.formatCodeSettings, ts.defaultOptions); + const { changes, commands } = this.languageService.getCombinedCodeFix({ type: "file", fileName: this.activeFile.fileName }, fixId, this.formatCodeSettings, ts.defaultPreferences); assert.deepEqual(commands, options.commands); assert(changes.every(c => c.fileName === this.activeFile.fileName), "TODO: support testing codefixes that touch multiple files"); this.applyChanges(changes); @@ -2482,7 +2482,7 @@ Actual: ${stringify(fullActual)}`); public verifyCodeFix(options: FourSlashInterface.VerifyCodeFixOptions) { const fileName = this.activeFile.fileName; - const actions = this.getCodeFixes(fileName, options.errorCode, options.options); + const actions = this.getCodeFixes(fileName, options.errorCode, options.preferences); let index = options.index; if (index === undefined) { if (!(actions && actions.length === 1)) { @@ -2521,7 +2521,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, options: ts.Options = ts.defaultOptions): ts.CodeFixAction[] { + private getCodeFixes(fileName: string, errorCode?: number, preferences: ts.UserPreferences = ts.defaultPreferences): ts.CodeFixAction[] { const diagnosticsForCodeFix = this.getDiagnostics(fileName, /*includeSuggestions*/ true).map(diagnostic => ({ start: diagnostic.start, length: diagnostic.length, @@ -2533,7 +2533,7 @@ Actual: ${stringify(fullActual)}`); return; } - return this.languageService.getCodeFixesAtPosition(fileName, diagnostic.start, diagnostic.start + diagnostic.length, [diagnostic.code], this.formatCodeSettings, options); + return this.languageService.getCodeFixesAtPosition(fileName, diagnostic.start, diagnostic.start + diagnostic.length, [diagnostic.code], this.formatCodeSettings, preferences); }); } @@ -2559,7 +2559,7 @@ Actual: ${stringify(fullActual)}`); } } - public verifyImportFixAtPosition(expectedTextArray: string[], errorCode: number | undefined, options: ts.Options | undefined) { + public verifyImportFixAtPosition(expectedTextArray: string[], errorCode: number | undefined, preferences: ts.UserPreferences | undefined) { const { fileName } = this.activeFile; const ranges = this.getRanges().filter(r => r.fileName === fileName); if (ranges.length !== 1) { @@ -2567,7 +2567,7 @@ Actual: ${stringify(fullActual)}`); } const range = ts.first(ranges); - const codeFixes = this.getCodeFixes(fileName, errorCode, options); + const codeFixes = this.getCodeFixes(fileName, errorCode, preferences); if (codeFixes.length === 0) { if (expectedTextArray.length !== 0) { @@ -2937,7 +2937,7 @@ Actual: ${stringify(fullActual)}`); public verifyApplicableRefactorAvailableAtMarker(negative: boolean, markerName: string) { const marker = this.getMarkerByName(markerName); - const applicableRefactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, marker.position, ts.defaultOptions); + const applicableRefactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, marker.position, ts.defaultPreferences); const isAvailable = applicableRefactors && applicableRefactors.length > 0; if (negative && isAvailable) { this.raiseError(`verifyApplicableRefactorAvailableAtMarker failed - expected no refactor at marker ${markerName} but found some.`); @@ -2957,7 +2957,7 @@ Actual: ${stringify(fullActual)}`); public verifyRefactorAvailable(negative: boolean, name: string, actionName?: string) { const selection = this.getSelection(); - let refactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, selection, ts.defaultOptions) || []; + let refactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, selection, ts.defaultPreferences) || []; refactors = refactors.filter(r => r.name === name && (actionName === undefined || r.actions.some(a => a.name === actionName))); const isAvailable = refactors.length > 0; @@ -2979,7 +2979,7 @@ Actual: ${stringify(fullActual)}`); public verifyRefactor({ name, actionName, refactors }: FourSlashInterface.VerifyRefactorOptions) { const selection = this.getSelection(); - const actualRefactors = (this.languageService.getApplicableRefactors(this.activeFile.fileName, selection, ts.defaultOptions) || ts.emptyArray) + const actualRefactors = (this.languageService.getApplicableRefactors(this.activeFile.fileName, selection, ts.defaultPreferences) || ts.emptyArray) .filter(r => r.name === name && r.actions.some(a => a.name === actionName)); this.assertObjectsEqual(actualRefactors, refactors); } @@ -2990,7 +2990,7 @@ Actual: ${stringify(fullActual)}`); throw new Error("Exactly one refactor range is allowed per test."); } - const applicableRefactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, ts.first(ranges), ts.defaultOptions); + const applicableRefactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, ts.first(ranges), ts.defaultPreferences); const isAvailable = applicableRefactors && applicableRefactors.length > 0; if (negative && isAvailable) { this.raiseError(`verifyApplicableRefactorAvailableForRange failed - expected no refactor but found some.`); @@ -3002,7 +3002,7 @@ Actual: ${stringify(fullActual)}`); public applyRefactor({ refactorName, actionName, actionDescription, newContent: newContentWithRenameMarker }: FourSlashInterface.ApplyRefactorOptions) { const range = this.getSelection(); - const refactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, range, ts.defaultOptions); + const refactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, range, ts.defaultPreferences); const refactorsWithName = refactors.filter(r => r.name === refactorName); if (refactorsWithName.length === 0) { this.raiseError(`The expected refactor: ${refactorName} is not available at the marker location.\nAvailable refactors: ${refactors.map(r => r.name)}`); @@ -3016,7 +3016,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.defaultOptions); + const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, this.formatCodeSettings, range, refactorName, actionName, ts.defaultPreferences); for (const edit of editInfo.edits) { this.applyEdits(edit.fileName, edit.textChanges, /*isFormattingEdit*/ false); } @@ -3061,14 +3061,14 @@ Actual: ${stringify(fullActual)}`); formattingOptions = formattingOptions || this.formatCodeSettings; const markerPos = this.getMarkerByName(markerName).position; - const applicableRefactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, markerPos, ts.defaultOptions); + const applicableRefactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, markerPos, ts.defaultPreferences); 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.defaultOptions); + const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, formattingOptions, markerPos, refactorNameToApply, actionName, ts.defaultPreferences); for (const edit of editInfo.edits) { this.applyEdits(edit.fileName, edit.textChanges, /*isFormattingEdit*/ false); @@ -4233,8 +4233,8 @@ namespace FourSlashInterface { this.state.applyCodeActionFromCompletion(markerName, options); } - public importFixAtPosition(expectedTextArray: string[], errorCode?: number, options?: ts.Options): void { - this.state.verifyImportFixAtPosition(expectedTextArray, errorCode, options); + public importFixAtPosition(expectedTextArray: string[], errorCode?: number, preferences?: ts.UserPreferences): void { + this.state.verifyImportFixAtPosition(expectedTextArray, errorCode, preferences); } public navigationBar(json: any, options?: { checkSpans?: boolean }) { @@ -4440,7 +4440,7 @@ namespace FourSlashInterface { this.state.printCurrentSignatureHelp(); } - public printCompletionListMembers(options: ts.Options | undefined) { + public printCompletionListMembers(options: ts.UserPreferences | undefined) { this.state.printCompletionListMembers(options); } @@ -4637,11 +4637,11 @@ namespace FourSlashInterface { } export type ExpectedCompletionEntry = string | { name: string, insertText?: string, replacementSpan?: FourSlash.Range }; - export interface CompletionsAtOptions extends Partial { + export interface CompletionsAtOptions extends Partial { isNewIdentifierLocation?: boolean; } - export interface VerifyCompletionListContainsOptions extends ts.Options { + export interface VerifyCompletionListContainsOptions extends ts.UserPreferences { sourceDisplay: string; isRecommended?: true; insertText?: string; @@ -4662,7 +4662,7 @@ namespace FourSlashInterface { description: string; errorCode?: number; index?: number; - options?: ts.Options; + preferences?: ts.UserPreferences; } export interface VerifyCodeFixAvailableOptions { @@ -4686,7 +4686,7 @@ namespace FourSlashInterface { name: string; source?: string; description: string; - options?: ts.Options; + preferences?: ts.UserPreferences; } export interface Diagnostic { diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index ac4bd251e774e..17788f6251d71 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -420,11 +420,11 @@ namespace Harness.LanguageService { getEncodedSemanticClassifications(fileName: string, span: ts.TextSpan): ts.Classifications { return unwrapJSONCallResult(this.shim.getEncodedSemanticClassifications(fileName, span.start, span.length)); } - getCompletionsAtPosition(fileName: string, position: number, options: ts.Options | undefined): ts.CompletionInfo { - return unwrapJSONCallResult(this.shim.getCompletionsAtPosition(fileName, position, options)); + getCompletionsAtPosition(fileName: string, position: number, preferences: ts.UserPreferences | undefined): ts.CompletionInfo { + return unwrapJSONCallResult(this.shim.getCompletionsAtPosition(fileName, position, preferences)); } - getCompletionEntryDetails(fileName: string, position: number, entryName: string, formatOptions: ts.FormatCodeOptions | undefined, source: string | undefined, options: ts.Options | undefined): ts.CompletionEntryDetails { - return unwrapJSONCallResult(this.shim.getCompletionEntryDetails(fileName, position, entryName, JSON.stringify(formatOptions), source, options)); + getCompletionEntryDetails(fileName: string, position: number, entryName: string, formatOptions: ts.FormatCodeOptions | undefined, source: string | undefined, preferences: ts.UserPreferences | undefined): ts.CompletionEntryDetails { + return unwrapJSONCallResult(this.shim.getCompletionEntryDetails(fileName, position, entryName, JSON.stringify(formatOptions), source, preferences)); } getCompletionEntrySymbol(): ts.Symbol { throw new Error("getCompletionEntrySymbol not implemented across the shim layer."); diff --git a/src/harness/unittests/extractTestHelpers.ts b/src/harness/unittests/extractTestHelpers.ts index 56bcdbc3ab9ed..eaf90a211ccdc 100644 --- a/src/harness/unittests/extractTestHelpers.ts +++ b/src/harness/unittests/extractTestHelpers.ts @@ -127,7 +127,7 @@ namespace ts { endPosition: selectionRange.end, host: notImplementedHost, formatContext: formatting.getFormatContext(testFormatOptions), - options: defaultOptions, + preferences: defaultPreferences, }; const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromRange(selectionRange)); assert.equal(rangeToExtract.errors, undefined, rangeToExtract.errors && "Range error: " + rangeToExtract.errors[0].messageText); @@ -191,7 +191,7 @@ namespace ts { endPosition: selectionRange.end, host: notImplementedHost, formatContext: formatting.getFormatContext(testFormatOptions), - options: defaultOptions, + preferences: defaultPreferences, }; const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromRange(selectionRange)); assert.isUndefined(rangeToExtract.errors, rangeToExtract.errors && "Range error: " + rangeToExtract.errors[0].messageText); diff --git a/src/harness/unittests/organizeImports.ts b/src/harness/unittests/organizeImports.ts index b11f35be7683f..de4a4722d67dc 100644 --- a/src/harness/unittests/organizeImports.ts +++ b/src/harness/unittests/organizeImports.ts @@ -193,7 +193,7 @@ export const Other = 1; content: "function F() { }", }; const languageService = makeLanguageService(testFile); - const changes = languageService.organizeImports({ type: "file", fileName: testFile.path }, testFormatOptions, defaultOptions); + const changes = languageService.organizeImports({ type: "file", fileName: testFile.path }, testFormatOptions, defaultPreferences); assert.isEmpty(changes); }); @@ -403,7 +403,7 @@ import { React, Other } from "react"; function runBaseline(baselinePath: string, testFile: TestFSWithWatch.FileOrFolder, ...otherFiles: TestFSWithWatch.FileOrFolder[]) { const { path: testPath, content: testContent } = testFile; const languageService = makeLanguageService(testFile, ...otherFiles); - const changes = languageService.organizeImports({ type: "file", fileName: testPath }, testFormatOptions, defaultOptions); + const changes = languageService.organizeImports({ type: "file", fileName: testPath }, testFormatOptions, defaultPreferences); assert.equal(changes.length, 1); assert.equal(changes[0].fileName, testPath); diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 63e7076d80286..0a619da1cd39a 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -1327,13 +1327,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, defaultOptions); + const completions1 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 2, defaultPreferences); // 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, defaultOptions); + const completions2 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 2, defaultPreferences); // 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'"); @@ -1359,11 +1359,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, defaultOptions); + const completions1 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 0, defaultPreferences); 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, defaultOptions); + const completions2 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 0, defaultPreferences); 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, ""); @@ -1968,7 +1968,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, defaultOptions); + let completions = project.getLanguageService().getCompletionsAtPosition(file1.path, 1, defaultPreferences); assert(completions && completions.entries[0].name === "hello", `expected entry hello to be in completion list`); // Close HTML file @@ -1982,7 +1982,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, defaultOptions); + completions = project.getLanguageService().getCompletionsAtPosition(file1.path, 5, defaultPreferences); assert(completions && completions.entries[0].name !== "hello", `unexpected hello entry in completion list`); }); diff --git a/src/server/client.ts b/src/server/client.ts index ff8a94f5d5f04..1a0912d378cc2 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -169,8 +169,8 @@ namespace ts.server { }; } - getCompletionsAtPosition(fileName: string, position: number, _settings: Options | undefined): CompletionInfo { - // Not passing along 'settings' because server should already have those from the 'configure' command + getCompletionsAtPosition(fileName: string, position: number, _preferences: UserPreferences | undefined): CompletionInfo { + // Not passing along 'preferences' because server should already have those from the 'configure' command const args: protocol.CompletionsRequestArgs = this.createFileLocationRequestArgs(fileName, position); const request = this.processRequest(CommandNames.Completions, args); diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index c7626125f88e5..ba0a80890fe8e 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -200,7 +200,7 @@ namespace ts.server { export interface HostConfiguration { formatCodeOptions: FormatCodeSettings; - options: Options; + preferences: UserPreferences; hostInfo: string; extraFileExtensions?: JsFileExtensionInfo[]; } @@ -439,7 +439,7 @@ namespace ts.server { this.hostConfiguration = { formatCodeOptions: getDefaultFormatCodeSettings(this.host), - options: defaultOptions, + preferences: defaultPreferences, hostInfo: "Unknown host", extraFileExtensions: [] }; @@ -690,9 +690,9 @@ namespace ts.server { return info && info.getFormatCodeSettings() || this.hostConfiguration.formatCodeOptions; } - getOptions(file: NormalizedPath): Options { + getPreferences(file: NormalizedPath): UserPreferences { const info = this.getScriptInfoForNormalizedPath(file); - return info && info.getOptions() || this.hostConfiguration.options; + return info && info.getPreferences() || this.hostConfiguration.preferences; } private onSourceFileChanged(fileName: string, eventKind: FileWatcherEventKind, path: Path) { @@ -1814,7 +1814,7 @@ namespace ts.server { if (args.file) { const info = this.getScriptInfoForNormalizedPath(toNormalizedPath(args.file)); if (info) { - info.setOptions(convertFormatOptions(args.formatOptions), args.options); + info.setOptions(convertFormatOptions(args.formatOptions), args.preferences); this.logger.info(`Host configuration update for file ${args.file}`); } } @@ -1827,8 +1827,8 @@ namespace ts.server { mergeMapLikes(this.hostConfiguration.formatCodeOptions, convertFormatOptions(args.formatOptions)); this.logger.info("Format host information updated"); } - if (args.options) { - mergeMapLikes(this.hostConfiguration.options, args.options); + if (args.preferences) { + mergeMapLikes(this.hostConfiguration.preferences, args.preferences); } if (args.extraFileExtensions) { this.hostConfiguration.extraFileExtensions = args.extraFileExtensions; diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 7f1bed7b7f0c6..9e179db8e5e55 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -1267,7 +1267,7 @@ namespace ts.server.protocol { */ formatOptions?: FormatCodeSettings; - options?: Options; + preferences?: UserPreferences; /** * The host's additional supported .js file extensions @@ -1744,11 +1744,11 @@ namespace ts.server.protocol { */ prefix?: string; /** - * @deprecated Use Options + * @deprecated Use UserPreferences.includeCompletionsForModuleExports */ includeExternalModuleExports?: boolean; /** - * @deprecated Use Options + * @deprecated Use UserPreferences.includeCompletionsWithInsertText */ includeInsertTextCompletions?: boolean; } @@ -2633,8 +2633,8 @@ namespace ts.server.protocol { insertSpaceBeforeTypeAnnotation?: boolean; } - export interface Options { - readonly quote?: "double" | "single"; + export interface UserPreferences { + readonly quotePreference?: "double" | "single"; /** * If enabled, TypeScript will search through all external modules' exports and add them to the completions list. * This affects lone identifier completions but not completions on the right hand side of `obj.`. @@ -2645,7 +2645,7 @@ namespace ts.server.protocol { * For those entries, The `insertText` and `replacementSpan` properties will be set to change from `.x` property access to `["x"]`. */ readonly includeCompletionsWithInsertText?: boolean; - readonly importModuleSpecifierPreference?: "relative" | "baseUrl"; + readonly importModuleSpecifierPreference?: "relative" | "non-relative"; } export interface CompilerOptions { diff --git a/src/server/scriptInfo.ts b/src/server/scriptInfo.ts index 0db6bf55295ed..843122e44c01f 100644 --- a/src/server/scriptInfo.ts +++ b/src/server/scriptInfo.ts @@ -210,7 +210,7 @@ namespace ts.server { */ readonly containingProjects: Project[] = []; private formatSettings: FormatCodeSettings | undefined; - private options: Options | undefined; + private preferences: UserPreferences | undefined; /* @internal */ fileWatcher: FileWatcher; @@ -300,7 +300,7 @@ namespace ts.server { } getFormatCodeSettings(): FormatCodeSettings { return this.formatSettings; } - getOptions(): Options { return this.options; } + getPreferences(): UserPreferences { return this.preferences; } attachToProject(project: Project): boolean { const isNew = !this.isAttached(project); @@ -393,7 +393,7 @@ namespace ts.server { } } - setOptions(formatSettings: FormatCodeSettings, options: Options): void { + setOptions(formatSettings: FormatCodeSettings, preferences: UserPreferences): void { if (formatSettings) { if (!this.formatSettings) { this.formatSettings = getDefaultFormatCodeSettings(this.host); @@ -401,11 +401,11 @@ namespace ts.server { mergeMapLikes(this.formatSettings, formatSettings); } - if (options) { - if (!this.options) { - this.options = clone(defaultOptions); + if (preferences) { + if (!this.preferences) { + this.preferences = clone(defaultPreferences); } - mergeMapLikes(this.options, options); + mergeMapLikes(this.preferences, preferences); } } diff --git a/src/server/session.ts b/src/server/session.ts index 64f452d5e3bba..1999360e34b06 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1270,7 +1270,7 @@ namespace ts.server { const position = this.getPosition(args, scriptInfo); const completions = project.getLanguageService().getCompletionsAtPosition(file, position, { - ...this.getOptions(file), + ...this.getPreferences(file), includeExternalModuleExports: args.includeExternalModuleExports, includeInsertTextCompletions: args.includeInsertTextCompletions }); @@ -1297,7 +1297,7 @@ namespace ts.server { const result = mapDefined(args.entryNames, entryName => { const { name, source } = typeof entryName === "string" ? { name: entryName, source: undefined } : entryName; - return project.getLanguageService().getCompletionEntryDetails(file, position, name, formattingOptions, source, this.getOptions(file)); + return project.getLanguageService().getCompletionEntryDetails(file, position, name, formattingOptions, source, this.getPreferences(file)); }); return simplifiedResult ? result.map(details => ({ ...details, codeActions: map(details.codeActions, action => this.mapCodeAction(project, action)) })) @@ -1595,7 +1595,7 @@ namespace ts.server { const { file, project } = this.getFileAndProject(args); const scriptInfo = project.getScriptInfoForNormalizedPath(file); const { position, textRange } = this.extractPositionAndRange(args, scriptInfo); - return project.getLanguageService().getApplicableRefactors(file, position || textRange, this.getOptions(file)); + return project.getLanguageService().getApplicableRefactors(file, position || textRange, this.getPreferences(file)); } private getEditsForRefactor(args: protocol.GetEditsForRefactorRequestArgs, simplifiedResult: boolean): RefactorEditInfo | protocol.RefactorEditInfo { @@ -1609,7 +1609,7 @@ namespace ts.server { position || textRange, args.refactor, args.action, - this.getOptions(file), + this.getPreferences(file), ); if (result === undefined) { @@ -1635,7 +1635,7 @@ namespace ts.server { private organizeImports({ scope }: protocol.OrganizeImportsRequestArgs, simplifiedResult: boolean): ReadonlyArray | ReadonlyArray { Debug.assert(scope.type === "file"); const { file, project } = this.getFileAndProject(scope.args); - const changes = project.getLanguageService().organizeImports({ type: "file", fileName: file }, this.getFormatOptions(file), this.getOptions(file)); + const changes = project.getLanguageService().organizeImports({ type: "file", fileName: file }, this.getFormatOptions(file), this.getPreferences(file)); if (simplifiedResult) { return this.mapTextChangesToCodeEdits(project, changes); } @@ -1653,7 +1653,7 @@ namespace ts.server { const scriptInfo = project.getScriptInfoForNormalizedPath(file); const { startPosition, endPosition } = this.getStartAndEndPosition(args, scriptInfo); - const codeActions = project.getLanguageService().getCodeFixesAtPosition(file, startPosition, endPosition, args.errorCodes, this.getFormatOptions(file), this.getOptions(file)); + const codeActions = project.getLanguageService().getCodeFixesAtPosition(file, startPosition, endPosition, args.errorCodes, this.getFormatOptions(file), this.getPreferences(file)); if (!codeActions) { return undefined; } @@ -1668,7 +1668,7 @@ namespace ts.server { private getCombinedCodeFix({ scope, fixId }: protocol.GetCombinedCodeFixRequestArgs, simplifiedResult: boolean): protocol.CombinedCodeActions | CombinedCodeActions { Debug.assert(scope.type === "file"); const { file, project } = this.getFileAndProject(scope.args); - const res = project.getLanguageService().getCombinedCodeFix({ type: "file", fileName: file }, fixId, this.getFormatOptions(file), this.getOptions(file)); + const res = project.getLanguageService().getCombinedCodeFix({ type: "file", fileName: file }, fixId, this.getFormatOptions(file), this.getPreferences(file)); if (simplifiedResult) { return { changes: this.mapTextChangesToCodeEdits(project, res.changes), commands: res.commands }; } @@ -2194,8 +2194,8 @@ namespace ts.server { return this.projectService.getFormatCodeOptions(file); } - private getOptions(file: NormalizedPath): Options { - return this.projectService.getOptions(file); + private getPreferences(file: NormalizedPath): UserPreferences { + return this.projectService.getPreferences(file); } } diff --git a/src/services/codeFixProvider.ts b/src/services/codeFixProvider.ts index b029fb6717243..74d3487a74c8d 100644 --- a/src/services/codeFixProvider.ts +++ b/src/services/codeFixProvider.ts @@ -11,7 +11,7 @@ namespace ts { sourceFile: SourceFile; program: Program; cancellationToken: CancellationToken; - options: Options; + preferences: UserPreferences; } export interface CodeFixAllContext extends CodeFixContextBase { diff --git a/src/services/codefixes/fixAddMissingMember.ts b/src/services/codefixes/fixAddMissingMember.ts index 33761fef3dcf8..b315dd0659fae 100644 --- a/src/services/codefixes/fixAddMissingMember.ts +++ b/src/services/codefixes/fixAddMissingMember.ts @@ -11,7 +11,7 @@ namespace ts.codefix { const info = getInfo(context.sourceFile, context.span.start, context.program.getTypeChecker()); if (!info) return undefined; const { classDeclaration, classDeclarationSourceFile, inJs, makeStatic, token, call } = info; - const methodCodeAction = call && getActionForMethodDeclaration(context, classDeclarationSourceFile, classDeclaration, token, call, makeStatic, inJs, context.options); + const methodCodeAction = call && getActionForMethodDeclaration(context, classDeclarationSourceFile, classDeclaration, token, call, makeStatic, inJs, context.preferences); const addMember = inJs ? singleElementArray(getActionsForAddMissingMemberInJavaScriptFile(context, classDeclarationSourceFile, classDeclaration, token.text, makeStatic)) : getActionsForAddMissingMemberInTypeScriptFile(context, classDeclarationSourceFile, classDeclaration, token, makeStatic); @@ -21,7 +21,7 @@ namespace ts.codefix { getAllCodeActions: context => { const seenNames = createMap(); return codeFixAll(context, errorCodes, (changes, diag) => { - const { program, options } = context; + const { program, preferences } = context; const info = getInfo(diag.file!, diag.start!, program.getTypeChecker()); if (!info) return; const { classDeclaration, classDeclarationSourceFile, inJs, makeStatic, token, call } = info; @@ -31,7 +31,7 @@ namespace ts.codefix { // Always prefer to add a method declaration if possible. if (call) { - addMethodDeclaration(changes, classDeclarationSourceFile, classDeclaration, token, call, makeStatic, inJs, options); + addMethodDeclaration(changes, classDeclarationSourceFile, classDeclaration, token, call, makeStatic, inJs, preferences); } else { if (inJs) { @@ -189,10 +189,10 @@ namespace ts.codefix { callExpression: CallExpression, makeStatic: boolean, inJs: boolean, - options: Options, + preferences: UserPreferences, ): CodeFixAction | undefined { const description = formatStringFromArgs(getLocaleSpecificMessage(makeStatic ? Diagnostics.Declare_static_method_0 : Diagnostics.Declare_method_0), [token.text]); - const changes = textChanges.ChangeTracker.with(context, t => addMethodDeclaration(t, classDeclarationSourceFile, classDeclaration, token, callExpression, makeStatic, inJs, options)); + const changes = textChanges.ChangeTracker.with(context, t => addMethodDeclaration(t, classDeclarationSourceFile, classDeclaration, token, callExpression, makeStatic, inJs, preferences)); return { description, changes, fixId }; } @@ -204,9 +204,9 @@ namespace ts.codefix { callExpression: CallExpression, makeStatic: boolean, inJs: boolean, - options: Options, + preferences: UserPreferences, ): void { - const methodDeclaration = createMethodFromCallExpression(callExpression, token.text, inJs, makeStatic, options); + const methodDeclaration = createMethodFromCallExpression(callExpression, token.text, inJs, makeStatic, preferences); changeTracker.insertNodeAtClassStart(classDeclarationSourceFile, classDeclaration, methodDeclaration); } } diff --git a/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts b/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts index f0ed745c5bdd4..cb33d1c1793ab 100644 --- a/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts +++ b/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts @@ -10,7 +10,7 @@ namespace ts.codefix { getCodeActions(context) { const { program, sourceFile, span } = context; const changes = textChanges.ChangeTracker.with(context, t => - addMissingMembers(getClass(sourceFile, span.start), sourceFile, program.getTypeChecker(), t, context.options)); + addMissingMembers(getClass(sourceFile, span.start), sourceFile, program.getTypeChecker(), t, context.preferences)); return changes.length === 0 ? undefined : [{ description: getLocaleSpecificMessage(Diagnostics.Implement_inherited_abstract_class), changes, fixId }]; }, fixIds: [fixId], @@ -19,7 +19,7 @@ namespace ts.codefix { return codeFixAll(context, errorCodes, (changes, diag) => { const classDeclaration = getClass(diag.file!, diag.start!); if (addToSeen(seenClassDeclarations, getNodeId(classDeclaration))) { - addMissingMembers(classDeclaration, context.sourceFile, context.program.getTypeChecker(), changes, context.options); + addMissingMembers(classDeclaration, context.sourceFile, context.program.getTypeChecker(), changes, context.preferences); } }); }, @@ -32,7 +32,7 @@ namespace ts.codefix { return cast(token.parent, isClassLike); } - function addMissingMembers(classDeclaration: ClassLikeDeclaration, sourceFile: SourceFile, checker: TypeChecker, changeTracker: textChanges.ChangeTracker, options: Options): void { + function addMissingMembers(classDeclaration: ClassLikeDeclaration, sourceFile: SourceFile, checker: TypeChecker, changeTracker: textChanges.ChangeTracker, preferences: UserPreferences): void { const extendsNode = getClassExtendsHeritageClauseElement(classDeclaration); const instantiatedExtendsType = checker.getTypeAtLocation(extendsNode); @@ -40,7 +40,7 @@ namespace ts.codefix { // so duplicates cannot occur. const abstractAndNonPrivateExtendsSymbols = checker.getPropertiesOfType(instantiatedExtendsType).filter(symbolPointsToNonPrivateAndAbstractMember); - createMissingMemberNodes(classDeclaration, abstractAndNonPrivateExtendsSymbols, checker, options, member => changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, member)); + createMissingMemberNodes(classDeclaration, abstractAndNonPrivateExtendsSymbols, checker, preferences, member => changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, member)); } function symbolPointsToNonPrivateAndAbstractMember(symbol: Symbol): boolean { diff --git a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts index 697e845ea5698..38cedbb2ce48f 100644 --- a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts +++ b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts @@ -10,7 +10,7 @@ namespace ts.codefix { const classDeclaration = getClass(sourceFile, span.start); const checker = program.getTypeChecker(); return mapDefined(getClassImplementsHeritageClauseElements(classDeclaration), implementedTypeNode => { - const changes = textChanges.ChangeTracker.with(context, t => addMissingDeclarations(checker, implementedTypeNode, sourceFile, classDeclaration, t, context.options)); + const changes = textChanges.ChangeTracker.with(context, t => addMissingDeclarations(checker, implementedTypeNode, sourceFile, classDeclaration, t, context.preferences)); if (changes.length === 0) return undefined; const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Implement_interface_0), [implementedTypeNode.getText()]); return { description, changes, fixId }; @@ -23,7 +23,7 @@ namespace ts.codefix { const classDeclaration = getClass(diag.file!, diag.start!); if (addToSeen(seenClassDeclarations, getNodeId(classDeclaration))) { for (const implementedTypeNode of getClassImplementsHeritageClauseElements(classDeclaration)) { - addMissingDeclarations(context.program.getTypeChecker(), implementedTypeNode, diag.file!, classDeclaration, changes, context.options); + addMissingDeclarations(context.program.getTypeChecker(), implementedTypeNode, diag.file!, classDeclaration, changes, context.preferences); } } }); @@ -40,7 +40,7 @@ namespace ts.codefix { sourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, changeTracker: textChanges.ChangeTracker, - options: Options, + preferences: UserPreferences, ): void { // Note that this is ultimately derived from a map indexed by symbol names, // so duplicates cannot occur. @@ -57,7 +57,7 @@ namespace ts.codefix { createMissingIndexSignatureDeclaration(implementedType, IndexKind.String); } - createMissingMemberNodes(classDeclaration, nonPrivateMembers, checker, options, member => changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, member)); + createMissingMemberNodes(classDeclaration, nonPrivateMembers, checker, preferences, member => changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, member)); function createMissingIndexSignatureDeclaration(type: InterfaceType, kind: IndexKind): void { const indexInfoOfKind = checker.getIndexInfoOfType(type, kind); diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts index 8fc94b733870d..35e75d831fdc8 100644 --- a/src/services/codefixes/helpers.ts +++ b/src/services/codefixes/helpers.ts @@ -6,11 +6,11 @@ namespace ts.codefix { * @param possiblyMissingSymbols The collection of symbols to filter and then get insertions for. * @returns Empty string iff there are no member insertions. */ - export function createMissingMemberNodes(classDeclaration: ClassLikeDeclaration, possiblyMissingSymbols: ReadonlyArray, checker: TypeChecker, options: Options, out: (node: ClassElement) => void): void { + export function createMissingMemberNodes(classDeclaration: ClassLikeDeclaration, possiblyMissingSymbols: ReadonlyArray, checker: TypeChecker, preferences: UserPreferences, out: (node: ClassElement) => void): void { const classMembers = classDeclaration.symbol.members; for (const symbol of possiblyMissingSymbols) { if (!classMembers.has(symbol.escapedName)) { - addNewNodeForMemberSymbol(symbol, classDeclaration, checker, options, out); + addNewNodeForMemberSymbol(symbol, classDeclaration, checker, preferences, out); } } } @@ -18,7 +18,7 @@ namespace ts.codefix { /** * @returns Empty string iff there we can't figure out a representation for `symbol` in `enclosingDeclaration`. */ - function addNewNodeForMemberSymbol(symbol: Symbol, enclosingDeclaration: ClassLikeDeclaration, checker: TypeChecker, options: Options, out: (node: Node) => void): void { + function addNewNodeForMemberSymbol(symbol: Symbol, enclosingDeclaration: ClassLikeDeclaration, checker: TypeChecker, preferences: UserPreferences, out: (node: Node) => void): void { const declarations = symbol.getDeclarations(); if (!(declarations && declarations.length)) { return undefined; @@ -63,7 +63,7 @@ namespace ts.codefix { if (declarations.length === 1) { Debug.assert(signatures.length === 1); const signature = signatures[0]; - outputMethod(signature, modifiers, name, createStubbedMethodBody(options)); + outputMethod(signature, modifiers, name, createStubbedMethodBody(preferences)); break; } @@ -74,11 +74,11 @@ namespace ts.codefix { if (declarations.length > signatures.length) { const signature = checker.getSignatureFromDeclaration(declarations[declarations.length - 1] as SignatureDeclaration); - outputMethod(signature, modifiers, name, createStubbedMethodBody(options)); + outputMethod(signature, modifiers, name, createStubbedMethodBody(preferences)); } else { Debug.assert(declarations.length === signatures.length); - out(createMethodImplementingSignatures(signatures, name, optional, modifiers, options)); + out(createMethodImplementingSignatures(signatures, name, optional, modifiers, preferences)); } break; } @@ -112,7 +112,7 @@ namespace ts.codefix { methodName: string, inJs: boolean, makeStatic: boolean, - options: Options, + preferences: UserPreferences, ): MethodDeclaration { return createMethod( /*decorators*/ undefined, @@ -124,7 +124,7 @@ namespace ts.codefix { createTypeParameterDeclaration(CharacterCodes.T + typeArguments.length - 1 <= CharacterCodes.Z ? String.fromCharCode(CharacterCodes.T + i) : `T${i}`)), /*parameters*/ createDummyParameters(args.length, /*names*/ undefined, /*minArgumentCount*/ undefined, inJs), /*type*/ inJs ? undefined : createKeywordTypeNode(SyntaxKind.AnyKeyword), - createStubbedMethodBody(options)); + createStubbedMethodBody(preferences)); } function createDummyParameters(argCount: number, names: string[] | undefined, minArgumentCount: number | undefined, inJs: boolean): ParameterDeclaration[] { @@ -148,7 +148,7 @@ namespace ts.codefix { name: PropertyName, optional: boolean, modifiers: ReadonlyArray | undefined, - options: Options, + preferences: UserPreferences, ): MethodDeclaration { /** This is *a* signature with the maximal number of arguments, * such that if there is a "maximal" signature without rest arguments, @@ -191,7 +191,7 @@ namespace ts.codefix { /*typeParameters*/ undefined, parameters, /*returnType*/ undefined, - options); + preferences); } function createStubbedMethod( @@ -201,7 +201,7 @@ namespace ts.codefix { typeParameters: ReadonlyArray | undefined, parameters: ReadonlyArray, returnType: TypeNode | undefined, - options: Options + preferences: UserPreferences ): MethodDeclaration { return createMethod( /*decorators*/ undefined, @@ -212,16 +212,16 @@ namespace ts.codefix { typeParameters, parameters, returnType, - createStubbedMethodBody(options)); + createStubbedMethodBody(preferences)); } - function createStubbedMethodBody(options: Options): Block { + function createStubbedMethodBody(preferences: UserPreferences): Block { return createBlock( [createThrow( createNew( createIdentifier("Error"), /*typeArguments*/ undefined, - [createLiteral("Method not implemented.", /*isSingleQuote*/ options.quote === "single")]))], + [createLiteral("Method not implemented.", /*isSingleQuote*/ preferences.quotePreference === "single")]))], /*multiline*/ true); } diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 83cc7f91c5a4b..761982958a131 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -30,7 +30,7 @@ namespace ts.codefix { compilerOptions: CompilerOptions; getCanonicalFileName: GetCanonicalFileName; cachedImportDeclarations?: ImportDeclarationMap; - options: Options; + preferences: UserPreferences; } function createCodeAction(descriptionDiagnostic: DiagnosticMessage, diagnosticArgs: string[], changes: FileTextChanges[]): CodeFixAction { @@ -55,7 +55,7 @@ namespace ts.codefix { getCanonicalFileName: createGetCanonicalFileName(useCaseSensitiveFileNames), symbolName, symbolToken, - options: context.options, + preferences: context.preferences, }; } @@ -97,13 +97,13 @@ namespace ts.codefix { formatContext: formatting.FormatContext, getCanonicalFileName: GetCanonicalFileName, symbolToken: Node | undefined, - options: Options, + preferences: UserPreferences, ): { readonly moduleSpecifier: string, readonly codeAction: CodeAction } { const exportInfos = getAllReExportingModules(exportedSymbol, checker, allSourceFiles); Debug.assert(exportInfos.some(info => info.moduleSymbol === moduleSymbol)); // We sort the best codefixes first, so taking `first` is best for completions. - const moduleSpecifier = first(getNewImportInfos(program, sourceFile, exportInfos, compilerOptions, getCanonicalFileName, host, options)).moduleSpecifier; - const ctx: ImportCodeFixContext = { host, program, checker, compilerOptions, sourceFile, formatContext, symbolName, getCanonicalFileName, symbolToken, options }; + const moduleSpecifier = first(getNewImportInfos(program, sourceFile, exportInfos, compilerOptions, getCanonicalFileName, host, preferences)).moduleSpecifier; + const ctx: ImportCodeFixContext = { host, program, checker, compilerOptions, sourceFile, formatContext, symbolName, getCanonicalFileName, symbolToken, preferences }; return { moduleSpecifier, codeAction: first(getCodeActionsForImport(exportInfos, ctx)) }; } function getAllReExportingModules(exportedSymbol: Symbol, checker: TypeChecker, allSourceFiles: ReadonlyArray): ReadonlyArray { @@ -184,12 +184,12 @@ namespace ts.codefix { } } - function getCodeActionForNewImport(context: SymbolContext & { options: Options }, { moduleSpecifier, importKind }: NewImportInfo): CodeFixAction { - const { sourceFile, symbolName, options } = context; + function getCodeActionForNewImport(context: SymbolContext & { preferences: UserPreferences }, { moduleSpecifier, importKind }: NewImportInfo): CodeFixAction { + const { sourceFile, symbolName, preferences } = context; const lastImportDeclaration = findLast(sourceFile.statements, isAnyImportSyntax); const moduleSpecifierWithoutQuotes = stripQuotes(moduleSpecifier); - const quotedModuleSpecifier = createLiteral(moduleSpecifierWithoutQuotes, shouldUseSingleQuote(sourceFile, options)); + const quotedModuleSpecifier = createLiteral(moduleSpecifierWithoutQuotes, shouldUseSingleQuote(sourceFile, preferences)); const importDecl = importKind !== ImportKind.Equals ? createImportDeclaration( /*decorators*/ undefined, @@ -217,9 +217,9 @@ namespace ts.codefix { return createCodeAction(Diagnostics.Import_0_from_module_1, [symbolName, moduleSpecifierWithoutQuotes], changes); } - function shouldUseSingleQuote(sourceFile: SourceFile, options: Options): boolean { - if (options.quote) { - return options.quote === "single"; + function shouldUseSingleQuote(sourceFile: SourceFile, preferences: UserPreferences): boolean { + if (preferences.quotePreference) { + return preferences.quotePreference === "single"; } else { const firstModuleSpecifier = firstOrUndefined(sourceFile.imports); @@ -252,7 +252,7 @@ namespace ts.codefix { compilerOptions: CompilerOptions, getCanonicalFileName: (file: string) => string, host: LanguageServiceHost, - options: Options, + preferences: UserPreferences, ): ReadonlyArray { const { baseUrl, paths, rootDirs } = compilerOptions; const addJsExtension = usesJsExtensionOnImports(sourceFile); @@ -268,7 +268,7 @@ namespace ts.codefix { } const relativePath = removeExtensionAndIndexPostFix(getRelativePath(moduleFileName, sourceDirectory, getCanonicalFileName), compilerOptions, addJsExtension); - if (!baseUrl || options.importModuleSpecifierPreference === "relative") { + if (!baseUrl || preferences.importModuleSpecifierPreference === "relative") { return [relativePath]; } @@ -285,11 +285,11 @@ namespace ts.codefix { } } - if (options.importModuleSpecifierPreference === "baseUrl") { + if (preferences.importModuleSpecifierPreference === "non-relative") { return [importRelativeToBaseUrl]; } - if (options.importModuleSpecifierPreference !== undefined) Debug.assertNever(options.importModuleSpecifierPreference); + if (preferences.importModuleSpecifierPreference !== undefined) Debug.assertNever(preferences.importModuleSpecifierPreference); if (isPathRelativeToParent(relativeToBaseUrl)) { return [relativePath]; @@ -586,7 +586,7 @@ namespace ts.codefix { const existingDeclaration = firstDefined(existingImports, newImportInfoFromExistingSpecifier); const newImportInfos = existingDeclaration ? [existingDeclaration] - : getNewImportInfos(ctx.program, ctx.sourceFile, exportInfos, ctx.compilerOptions, ctx.getCanonicalFileName, ctx.host, ctx.options); + : getNewImportInfos(ctx.program, ctx.sourceFile, exportInfos, ctx.compilerOptions, ctx.getCanonicalFileName, ctx.host, ctx.preferences); return newImportInfos.map(info => getCodeActionForNewImport(ctx, info)); } diff --git a/src/services/completions.ts b/src/services/completions.ts index 49eaf86a2914f..c6d964573aba2 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -32,7 +32,7 @@ namespace ts.Completions { sourceFile: SourceFile, position: number, allSourceFiles: ReadonlyArray, - options: Options, + preferences: UserPreferences, ): CompletionInfo | undefined { if (isInReferenceComment(sourceFile, position)) { const entries = PathCompletions.getTripleSlashReferenceCompletion(sourceFile, position, compilerOptions, host); @@ -44,7 +44,7 @@ namespace ts.Completions { if (isInString(sourceFile, position, contextToken)) { return !contextToken || !isStringLiteralLike(contextToken) ? undefined - : convertStringLiteralCompletions(getStringLiteralCompletionEntries(sourceFile, contextToken, position, typeChecker, compilerOptions, host), sourceFile, typeChecker, log, options); + : convertStringLiteralCompletions(getStringLiteralCompletionEntries(sourceFile, contextToken, position, typeChecker, compilerOptions, host), sourceFile, typeChecker, log, preferences); } if (contextToken && isBreakOrContinueStatement(contextToken.parent) @@ -52,14 +52,14 @@ namespace ts.Completions { return getLabelCompletionAtPosition(contextToken.parent); } - const completionData = getCompletionData(typeChecker, log, sourceFile, position, allSourceFiles, options, compilerOptions.target); + const completionData = getCompletionData(typeChecker, log, sourceFile, position, allSourceFiles, preferences, compilerOptions.target); if (!completionData) { return undefined; } switch (completionData.kind) { case CompletionDataKind.Data: - return completionInfoFromData(sourceFile, typeChecker, compilerOptions, log, completionData, options); + return completionInfoFromData(sourceFile, typeChecker, compilerOptions, log, completionData, preferences); case CompletionDataKind.JsDocTagName: // If the current position is a jsDoc tag name, only tag names should be provided for completion return jsdocCompletionInfo(JsDoc.getJSDocTagNameCompletions()); @@ -73,7 +73,7 @@ namespace ts.Completions { } } - function convertStringLiteralCompletions(completion: StringLiteralCompletion | undefined, sourceFile: SourceFile, checker: TypeChecker, log: Log, options: Options): CompletionInfo | undefined { + function convertStringLiteralCompletions(completion: StringLiteralCompletion | undefined, sourceFile: SourceFile, checker: TypeChecker, log: Log, preferences: UserPreferences): CompletionInfo | undefined { if (completion === undefined) { return undefined; } @@ -82,7 +82,7 @@ namespace ts.Completions { return convertPathCompletions(completion.paths); case StringLiteralCompletionKind.Properties: { const entries: CompletionEntry[] = []; - getCompletionEntriesFromSymbols(completion.symbols, entries, sourceFile, sourceFile, checker, ScriptTarget.ESNext, log, CompletionKind.String, options); // Target will not be used, so arbitrary + getCompletionEntriesFromSymbols(completion.symbols, entries, sourceFile, sourceFile, checker, ScriptTarget.ESNext, log, CompletionKind.String, preferences); // Target will not be used, so arbitrary return { isGlobalCompletion: false, isMemberCompletion: true, isNewIdentifierLocation: true, entries }; } case StringLiteralCompletionKind.Types: { @@ -105,7 +105,7 @@ namespace ts.Completions { return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, entries }; } - function completionInfoFromData(sourceFile: SourceFile, typeChecker: TypeChecker, compilerOptions: CompilerOptions, log: Log, completionData: CompletionData, options: Options): CompletionInfo { + function completionInfoFromData(sourceFile: SourceFile, typeChecker: TypeChecker, compilerOptions: CompilerOptions, log: Log, completionData: CompletionData, preferences: UserPreferences): CompletionInfo { const { symbols, completionKind, isInSnippetScope, isNewIdentifierLocation, location, propertyAccessToConvert, keywordFilters, symbolToOriginInfoMap, recommendedCompletion, isJsxInitializer } = completionData; if (sourceFile.languageVariant === LanguageVariant.JSX && location && location.parent && isJsxClosingElement(location.parent)) { @@ -127,7 +127,7 @@ namespace ts.Completions { const entries: CompletionEntry[] = []; if (isSourceFileJavaScript(sourceFile)) { - const uniqueNames = getCompletionEntriesFromSymbols(symbols, entries, location, sourceFile, typeChecker, compilerOptions.target, log, completionKind, options, propertyAccessToConvert, isJsxInitializer, recommendedCompletion, symbolToOriginInfoMap); + const uniqueNames = getCompletionEntriesFromSymbols(symbols, entries, location, sourceFile, typeChecker, compilerOptions.target, log, completionKind, preferences, propertyAccessToConvert, isJsxInitializer, recommendedCompletion, symbolToOriginInfoMap); getJavaScriptCompletionEntries(sourceFile, location.pos, uniqueNames, compilerOptions.target, entries); } else { @@ -135,7 +135,7 @@ namespace ts.Completions { return undefined; } - getCompletionEntriesFromSymbols(symbols, entries, location, sourceFile, typeChecker, compilerOptions.target, log, completionKind, options, propertyAccessToConvert, isJsxInitializer, recommendedCompletion, symbolToOriginInfoMap); + getCompletionEntriesFromSymbols(symbols, entries, location, sourceFile, typeChecker, compilerOptions.target, log, completionKind, preferences, propertyAccessToConvert, isJsxInitializer, recommendedCompletion, symbolToOriginInfoMap); } // TODO add filter for keyword based on type/value/namespace and also location @@ -196,7 +196,7 @@ namespace ts.Completions { recommendedCompletion: Symbol | undefined, propertyAccessToConvert: PropertyAccessExpression | undefined, isJsxInitializer: IsJsxInitializer, - options: Options, + preferences: UserPreferences, ): CompletionEntry | undefined { const info = getCompletionEntryDisplayNameForSymbol(symbol, target, origin, kind); if (!info) { @@ -206,12 +206,12 @@ namespace ts.Completions { let insertText: string | undefined; let replacementSpan: TextSpan | undefined; - if (options.includeCompletionsWithInsertText) { + if (preferences.includeCompletionsWithInsertText) { if (origin && origin.type === "this-type") { - insertText = needsConvertPropertyAccess ? `this[${quote(name, options)}]` : `this.${name}`; + insertText = needsConvertPropertyAccess ? `this[${quote(name, preferences)}]` : `this.${name}`; } else if (needsConvertPropertyAccess) { - insertText = `[${quote(name, options)}]`; + insertText = `[${quote(name, preferences)}]`; const dot = findChildOfKind(propertyAccessToConvert!, SyntaxKind.DotToken, sourceFile)!; // If the text after the '.' starts with this name, write over it. Else, add new text. const end = startsWith(name, propertyAccessToConvert!.name.text) ? propertyAccessToConvert!.name.end : dot.end; @@ -227,7 +227,7 @@ namespace ts.Completions { } } - if (insertText !== undefined && !options.includeCompletionsWithInsertText) { + if (insertText !== undefined && !preferences.includeCompletionsWithInsertText) { return undefined; } @@ -252,16 +252,16 @@ namespace ts.Completions { }; } - function quote(text: string, options: Options): string { + function quote(text: string, preferences: UserPreferences): string { const quoted = JSON.stringify(text); - switch (options.quote) { + switch (preferences.quotePreference) { case undefined: case "double": return quoted; case "single": return `'${stripQuotes(quoted).replace("'", "\\'").replace('\\"', '"')}'`; default: - return Debug.assertNever(options.quote); + return Debug.assertNever(preferences.quotePreference); } } @@ -287,7 +287,7 @@ namespace ts.Completions { target: ScriptTarget, log: Log, kind: CompletionKind, - options: Options, + preferences: UserPreferences, propertyAccessToConvert?: PropertyAccessExpression | undefined, isJsxInitializer?: IsJsxInitializer, recommendedCompletion?: Symbol, @@ -301,7 +301,7 @@ namespace ts.Completions { const uniques = createMap(); for (const symbol of symbols) { const origin = symbolToOriginInfoMap ? symbolToOriginInfoMap[getSymbolId(symbol)] : undefined; - const entry = createCompletionEntry(symbol, location, sourceFile, typeChecker, target, kind, origin, recommendedCompletion, propertyAccessToConvert, isJsxInitializer, options); + const entry = createCompletionEntry(symbol, location, sourceFile, typeChecker, target, kind, origin, recommendedCompletion, propertyAccessToConvert, isJsxInitializer, preferences); if (!entry) { continue; } @@ -524,7 +524,7 @@ namespace ts.Completions { host: LanguageServiceHost, formatContext: formatting.FormatContext, getCanonicalFileName: GetCanonicalFileName, - options: Options, + preferences: UserPreferences, ): CompletionEntryDetails { const typeChecker = program.getTypeChecker(); const { name } = entryId; @@ -546,7 +546,7 @@ namespace ts.Completions { } case "symbol": { const { symbol, location, symbolToOriginInfoMap, previousToken } = symbolCompletion; - const { codeActions, sourceDisplay } = getCompletionEntryCodeActionsAndSourceDisplay(symbolToOriginInfoMap, symbol, program, typeChecker, host, compilerOptions, sourceFile, previousToken, formatContext, getCanonicalFileName, allSourceFiles, options); + const { codeActions, sourceDisplay } = getCompletionEntryCodeActionsAndSourceDisplay(symbolToOriginInfoMap, symbol, program, typeChecker, host, compilerOptions, sourceFile, previousToken, formatContext, getCanonicalFileName, allSourceFiles, preferences); const kindModifiers = SymbolDisplay.getSymbolModifiers(symbol); const { displayParts, documentation, symbolKind, tags } = SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker, symbol, sourceFile, location, location, SemanticMeaning.All); return { name, kindModifiers, kind: symbolKind, displayParts, documentation, tags, codeActions, source: sourceDisplay }; @@ -586,7 +586,7 @@ namespace ts.Completions { formatContext: formatting.FormatContext, getCanonicalFileName: GetCanonicalFileName, allSourceFiles: ReadonlyArray, - options: Options, + preferences: UserPreferences, ): CodeActionsAndSourceDisplay { const symbolOriginInfo = symbolToOriginInfoMap[getSymbolId(symbol)]; if (!symbolOriginInfo || symbolOriginInfo.type !== "export") { @@ -608,7 +608,7 @@ namespace ts.Completions { formatContext, getCanonicalFileName, previousToken, - options); + preferences); return { sourceDisplay: [textPart(moduleSpecifier)], codeActions: [codeAction] }; } @@ -735,7 +735,7 @@ namespace ts.Completions { sourceFile: SourceFile, position: number, allSourceFiles: ReadonlyArray, - options: Pick, + preferences: Pick, target: ScriptTarget, ): CompletionData | Request | undefined { let start = timestamp(); @@ -1140,7 +1140,7 @@ namespace ts.Completions { symbols = Debug.assertEachDefined(typeChecker.getSymbolsInScope(scopeNode, symbolMeanings), "getSymbolsInScope() should all be defined"); // Need to insert 'this.' before properties of `this` type, so only do that if `includeInsertTextCompletions` - if (options.includeCompletionsWithInsertText && scopeNode.kind !== SyntaxKind.SourceFile) { + if (preferences.includeCompletionsWithInsertText && scopeNode.kind !== SyntaxKind.SourceFile) { const thisType = typeChecker.tryGetThisTypeAt(scopeNode); if (thisType) { for (const symbol of getPropertiesForCompletion(thisType, typeChecker, /*isForAccess*/ true)) { @@ -1151,7 +1151,7 @@ namespace ts.Completions { } // Don't suggest import completions for a commonjs-only module - if (options.includeCompletionsForModuleExports && !(sourceFile.commonJsModuleIndicator && !sourceFile.externalModuleIndicator)) { + if (preferences.includeCompletionsForModuleExports && !(sourceFile.commonJsModuleIndicator && !sourceFile.externalModuleIndicator)) { getSymbolsFromOtherSourceFileExports(symbols, previousToken && isIdentifier(previousToken) ? previousToken.text : "", target); } filterGlobalCompletion(symbols); diff --git a/src/services/organizeImports.ts b/src/services/organizeImports.ts index eb6fe5174fd9b..f6489fce6d208 100644 --- a/src/services/organizeImports.ts +++ b/src/services/organizeImports.ts @@ -12,7 +12,7 @@ namespace ts.OrganizeImports { formatContext: formatting.FormatContext, host: LanguageServiceHost, program: Program, - _options: Options, + _preferences: UserPreferences, ) { const changeTracker = textChanges.ChangeTracker.fromContext({ host, formatContext }); diff --git a/src/services/refactorProvider.ts b/src/services/refactorProvider.ts index 10cba3c3174d2..f3504ed89b82d 100644 --- a/src/services/refactorProvider.ts +++ b/src/services/refactorProvider.ts @@ -14,7 +14,7 @@ namespace ts { endPosition?: number; program: Program; cancellationToken?: CancellationToken; - options: Options; + preferences: UserPreferences; } export namespace refactor { diff --git a/src/services/services.ts b/src/services/services.ts index 6f04f5fbd7555..a3084ba8aac11 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1430,10 +1430,10 @@ namespace ts { return [...program.getOptionsDiagnostics(cancellationToken), ...program.getGlobalDiagnostics(cancellationToken)]; } - function getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions = defaultOptions): CompletionInfo { + function getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions = defaultPreferences): CompletionInfo { // Convert from deprecated options names to new names - const fullOptions: Options = { - ...identity(options), // avoid excess property check + const fullPreferences: UserPreferences = { + ...identity(options), // avoid excess property check includeCompletionsForModuleExports: options.includeCompletionsForModuleExports || options.includeExternalModuleExports, includeCompletionsWithInsertText: options.includeCompletionsWithInsertText || options.includeInsertTextCompletions, }; @@ -1446,10 +1446,10 @@ namespace ts { getValidSourceFile(fileName), position, program.getSourceFiles(), - fullOptions); + fullPreferences); } - function getCompletionEntryDetails(fileName: string, position: number, name: string, formattingOptions: FormatCodeSettings | undefined, source: string | undefined, options: Options = defaultOptions): CompletionEntryDetails { + function getCompletionEntryDetails(fileName: string, position: number, name: string, formattingOptions: FormatCodeSettings | undefined, source: string | undefined, preferences: UserPreferences = defaultPreferences): CompletionEntryDetails { synchronizeHostData(); return Completions.getCompletionEntryDetails( program, @@ -1462,7 +1462,7 @@ namespace ts { host, formattingOptions && formatting.getFormatContext(formattingOptions), getCanonicalFileName, - options); + preferences); } function getCompletionEntrySymbol(fileName: string, position: number, name: string, source?: string): Symbol { @@ -1827,7 +1827,7 @@ namespace ts { return []; } - function getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings, options: Options = defaultOptions): ReadonlyArray { + function getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings, preferences: UserPreferences = defaultPreferences): ReadonlyArray { synchronizeHostData(); const sourceFile = getValidSourceFile(fileName); const span = createTextSpanFromBounds(start, end); @@ -1835,26 +1835,26 @@ namespace ts { return flatMap(deduplicate(errorCodes, equateValues, compareValues), errorCode => { cancellationToken.throwIfCancellationRequested(); - return codefix.getFixes({ errorCode, sourceFile, span, program, host, cancellationToken, formatContext, options }); + return codefix.getFixes({ errorCode, sourceFile, span, program, host, cancellationToken, formatContext, preferences }); }); } - function getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, options: Options = defaultOptions): CombinedCodeActions { + function getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, preferences: UserPreferences = defaultPreferences): CombinedCodeActions { synchronizeHostData(); Debug.assert(scope.type === "file"); const sourceFile = getValidSourceFile(scope.fileName); const formatContext = formatting.getFormatContext(formatOptions); - return codefix.getAllFixes({ fixId, sourceFile, program, host, cancellationToken, formatContext, options }); + return codefix.getAllFixes({ fixId, sourceFile, program, host, cancellationToken, formatContext, preferences }); } - function organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, options: Options = defaultOptions): ReadonlyArray { + function organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, preferences: UserPreferences = defaultPreferences): ReadonlyArray { synchronizeHostData(); Debug.assert(scope.type === "file"); const sourceFile = getValidSourceFile(scope.fileName); const formatContext = formatting.getFormatContext(formatOptions); - return OrganizeImports.organizeImports(sourceFile, formatContext, host, program, options); + return OrganizeImports.organizeImports(sourceFile, formatContext, host, program, preferences); } function applyCodeActionCommand(action: CodeActionCommand): Promise; @@ -2078,7 +2078,7 @@ namespace ts { return Rename.getRenameInfo(program.getTypeChecker(), defaultLibFileName, getCanonicalFileName, getValidSourceFile(fileName), position); } - function getRefactorContext(file: SourceFile, positionOrRange: number | TextRange, options: Options, formatOptions?: FormatCodeSettings): RefactorContext { + function getRefactorContext(file: SourceFile, positionOrRange: number | TextRange, preferences: UserPreferences, formatOptions?: FormatCodeSettings): RefactorContext { const [startPosition, endPosition] = typeof positionOrRange === "number" ? [positionOrRange, undefined] : [positionOrRange.pos, positionOrRange.end]; return { file, @@ -2088,14 +2088,14 @@ namespace ts { host, formatContext: formatting.getFormatContext(formatOptions), cancellationToken, - options, + preferences, }; } - function getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, options: Options = defaultOptions): ApplicableRefactorInfo[] { + function getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences = defaultPreferences): ApplicableRefactorInfo[] { synchronizeHostData(); const file = getValidSourceFile(fileName); - return refactor.getApplicableRefactors(getRefactorContext(file, positionOrRange, options)); + return refactor.getApplicableRefactors(getRefactorContext(file, positionOrRange, preferences)); } function getEditsForRefactor( @@ -2104,12 +2104,12 @@ namespace ts { positionOrRange: number | TextRange, refactorName: string, actionName: string, - options: Options = defaultOptions, + preferences: UserPreferences = defaultPreferences, ): RefactorEditInfo { synchronizeHostData(); const file = getValidSourceFile(fileName); - return refactor.getEditsForRefactor(getRefactorContext(file, positionOrRange, options, formatOptions), refactorName, actionName); + return refactor.getEditsForRefactor(getRefactorContext(file, positionOrRange, preferences, formatOptions), refactorName, actionName); } return { diff --git a/src/services/shims.ts b/src/services/shims.ts index d2eb9f1e31028..42311843ea10e 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -152,8 +152,8 @@ namespace ts { getEncodedSyntacticClassifications(fileName: string, start: number, length: number): string; getEncodedSemanticClassifications(fileName: string, start: number, length: number): string; - getCompletionsAtPosition(fileName: string, position: number, options: Options | undefined): string; - getCompletionEntryDetails(fileName: string, position: number, entryName: string, formatOptions: string/*Services.FormatCodeOptions*/ | undefined, source: string | undefined, options: Options | undefined): string; + getCompletionsAtPosition(fileName: string, position: number, preferences: UserPreferences | undefined): string; + getCompletionEntryDetails(fileName: string, position: number, entryName: string, formatOptions: string/*Services.FormatCodeOptions*/ | undefined, source: string | undefined, preferences: UserPreferences | undefined): string; getQuickInfoAtPosition(fileName: string, position: number): string; @@ -913,20 +913,20 @@ namespace ts { * to provide at the given source position and providing a member completion * list if requested. */ - public getCompletionsAtPosition(fileName: string, position: number, options: Options | undefined) { + public getCompletionsAtPosition(fileName: string, position: number, preferences: UserPreferences | undefined) { return this.forwardJSONCall( - `getCompletionsAtPosition('${fileName}', ${position}, ${options})`, - () => this.languageService.getCompletionsAtPosition(fileName, position, options) + `getCompletionsAtPosition('${fileName}', ${position}, ${preferences})`, + () => this.languageService.getCompletionsAtPosition(fileName, position, preferences) ); } /** Get a string based representation of a completion list entry details */ - public getCompletionEntryDetails(fileName: string, position: number, entryName: string, formatOptions: string/*Services.FormatCodeOptions*/ | undefined, source: string | undefined, options: Options | undefined) { + public getCompletionEntryDetails(fileName: string, position: number, entryName: string, formatOptions: string/*Services.FormatCodeOptions*/ | undefined, source: string | undefined, preferences: UserPreferences | undefined) { return this.forwardJSONCall( `getCompletionEntryDetails('${fileName}', ${position}, '${entryName}')`, () => { const localOptions: FormatCodeOptions = formatOptions === undefined ? undefined : JSON.parse(formatOptions); - return this.languageService.getCompletionEntryDetails(fileName, position, entryName, localOptions, source, options); + return this.languageService.getCompletionEntryDetails(fileName, position, entryName, localOptions, source, preferences); } ); } diff --git a/src/services/types.ts b/src/services/types.ts index 6ed994218efbc..f37d3cb592c8b 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -214,14 +214,14 @@ namespace ts { installPackage?(options: InstallPackageOptions): Promise; } - export interface Options { - readonly quote?: "double" | "single"; + export interface UserPreferences { + readonly quotePreference?: "double" | "single"; readonly includeCompletionsForModuleExports?: boolean; readonly includeCompletionsWithInsertText?: boolean; - readonly importModuleSpecifierPreference?: "relative" | "baseUrl"; + readonly importModuleSpecifierPreference?: "relative" | "non-relative"; } /* @internal */ - export const defaultOptions: Options = {}; + export const defaultPreferences: UserPreferences = {}; // // Public services of a language service instance associated @@ -260,7 +260,7 @@ namespace ts { name: string, formatOptions: FormatCodeOptions | FormatCodeSettings | undefined, source: string | undefined, - options: Options | undefined, + preferences: UserPreferences | undefined, ): CompletionEntryDetails; getCompletionEntrySymbol(fileName: string, position: number, name: string, source: string | undefined): Symbol; @@ -306,8 +306,8 @@ namespace ts { getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan; - getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings, options: Options): ReadonlyArray; - getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, options: Options): CombinedCodeActions; + getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings, preferences: UserPreferences): ReadonlyArray; + getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, preferences: UserPreferences): CombinedCodeActions; applyCodeActionCommand(action: CodeActionCommand): Promise; applyCodeActionCommand(action: CodeActionCommand[]): Promise; applyCodeActionCommand(action: CodeActionCommand | CodeActionCommand[]): Promise; @@ -317,9 +317,9 @@ namespace ts { applyCodeActionCommand(fileName: string, action: CodeActionCommand[]): Promise; /** @deprecated `fileName` will be ignored */ applyCodeActionCommand(fileName: string, action: CodeActionCommand | CodeActionCommand[]): Promise; - getApplicableRefactors(fileName: string, positionOrRaneg: number | TextRange, options: Options | undefined): ApplicableRefactorInfo[]; - getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string, options: Options | undefined): RefactorEditInfo | undefined; - organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, options: Options | undefined): ReadonlyArray; + getApplicableRefactors(fileName: string, positionOrRaneg: number | TextRange, preferences: UserPreferences | undefined): ApplicableRefactorInfo[]; + getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string, preferences: UserPreferences | undefined): RefactorEditInfo | undefined; + organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): ReadonlyArray; getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean): EmitOutput; @@ -340,8 +340,8 @@ namespace ts { export type OrganizeImportsScope = CombinedCodeFixScope; - /** @deprecated Use Options */ - export interface GetCompletionsAtPositionOptions extends Options { + /** @deprecated Use UserPreferences */ + export interface GetCompletionsAtPositionOptions extends UserPreferences { /** @deprecated Use includeCompletionsForModuleExports */ includeExternalModuleExports?: boolean; /** @deprecated Use includeCompletionsWithInsertText */ diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 5c22cbeb8be21..9c6d86bb68463 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -4067,11 +4067,11 @@ declare namespace ts { isKnownTypesPackageName?(name: string): boolean; installPackage?(options: InstallPackageOptions): Promise; } - interface Options { - readonly quote?: "double" | "single"; + interface UserPreferences { + readonly quotePreference?: "double" | "single"; readonly includeCompletionsForModuleExports?: boolean; readonly includeCompletionsWithInsertText?: boolean; - readonly importModuleSpecifierPreference?: "relative" | "baseUrl"; + readonly importModuleSpecifierPreference?: "relative" | "non-relative"; } interface LanguageService { cleanupSemanticCache(): void; @@ -4090,7 +4090,7 @@ declare namespace ts { getEncodedSyntacticClassifications(fileName: string, span: TextSpan): Classifications; getEncodedSemanticClassifications(fileName: string, span: TextSpan): Classifications; getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions | undefined): CompletionInfo; - getCompletionEntryDetails(fileName: string, position: number, name: string, formatOptions: FormatCodeOptions | FormatCodeSettings | undefined, source: string | undefined, options: Options | undefined): CompletionEntryDetails; + getCompletionEntryDetails(fileName: string, position: number, name: string, formatOptions: FormatCodeOptions | FormatCodeSettings | undefined, source: string | undefined, preferences: UserPreferences | undefined): CompletionEntryDetails; getCompletionEntrySymbol(fileName: string, position: number, name: string, source: string | undefined): Symbol; getQuickInfoAtPosition(fileName: string, position: number): QuickInfo; getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): TextSpan; @@ -4120,8 +4120,8 @@ declare namespace ts { getDocCommentTemplateAtPosition(fileName: string, position: number): TextInsertion; isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): boolean; getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan; - getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings, options: Options): ReadonlyArray; - getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, options: Options): CombinedCodeActions; + getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings, preferences: UserPreferences): ReadonlyArray; + getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, preferences: UserPreferences): CombinedCodeActions; applyCodeActionCommand(action: CodeActionCommand): Promise; applyCodeActionCommand(action: CodeActionCommand[]): Promise; applyCodeActionCommand(action: CodeActionCommand | CodeActionCommand[]): Promise; @@ -4131,9 +4131,9 @@ declare namespace ts { applyCodeActionCommand(fileName: string, action: CodeActionCommand[]): Promise; /** @deprecated `fileName` will be ignored */ applyCodeActionCommand(fileName: string, action: CodeActionCommand | CodeActionCommand[]): Promise; - getApplicableRefactors(fileName: string, positionOrRaneg: number | TextRange, options: Options | undefined): ApplicableRefactorInfo[]; - getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string, options: Options | undefined): RefactorEditInfo | undefined; - organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, options: Options | undefined): ReadonlyArray; + getApplicableRefactors(fileName: string, positionOrRaneg: number | TextRange, preferences: UserPreferences | undefined): ApplicableRefactorInfo[]; + getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string, preferences: UserPreferences | undefined): RefactorEditInfo | undefined; + organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): ReadonlyArray; getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean): EmitOutput; getProgram(): Program; dispose(): void; @@ -4143,8 +4143,8 @@ declare namespace ts { fileName: string; } type OrganizeImportsScope = CombinedCodeFixScope; - /** @deprecated Use Options */ - interface GetCompletionsAtPositionOptions extends Options { + /** @deprecated Use UserPreferences */ + interface GetCompletionsAtPositionOptions extends UserPreferences { /** @deprecated Use includeCompletionsForModuleExports */ includeExternalModuleExports?: boolean; /** @deprecated Use includeCompletionsWithInsertText */ @@ -5965,7 +5965,7 @@ declare namespace ts.server.protocol { * The format options to use during formatting and other code editing features. */ formatOptions?: FormatCodeSettings; - options?: Options; + preferences?: UserPreferences; /** * The host's additional supported .js file extensions */ @@ -6338,11 +6338,11 @@ declare namespace ts.server.protocol { */ prefix?: string; /** - * @deprecated Use Options + * @deprecated Use UserPreferences.includeCompletionsForModuleExports */ includeExternalModuleExports?: boolean; /** - * @deprecated Use Options + * @deprecated Use UserPreferences.includeCompletionsWithInsertText */ includeInsertTextCompletions?: boolean; } @@ -7101,8 +7101,8 @@ declare namespace ts.server.protocol { placeOpenBraceOnNewLineForControlBlocks?: boolean; insertSpaceBeforeTypeAnnotation?: boolean; } - interface Options { - readonly quote?: "double" | "single"; + interface UserPreferences { + readonly quotePreference?: "double" | "single"; /** * If enabled, TypeScript will search through all external modules' exports and add them to the completions list. * This affects lone identifier completions but not completions on the right hand side of `obj.`. @@ -7113,7 +7113,7 @@ declare namespace ts.server.protocol { * For those entries, The `insertText` and `replacementSpan` properties will be set to change from `.x` property access to `["x"]`. */ readonly includeCompletionsWithInsertText?: boolean; - readonly importModuleSpecifierPreference?: "relative" | "baseUrl"; + readonly importModuleSpecifierPreference?: "relative" | "non-relative"; } interface CompilerOptions { allowJs?: boolean; @@ -7381,7 +7381,7 @@ declare namespace ts.server { executeCommand(request: protocol.Request): HandlerResponse; onMessage(message: string): void; private getFormatOptions(file); - private getOptions(file); + private getPreferences(file); } interface HandlerResponse { response?: {}; @@ -7400,7 +7400,7 @@ declare namespace ts.server { */ readonly containingProjects: Project[]; private formatSettings; - private options; + private preferences; private textStorage; constructor(host: ServerHost, fileName: NormalizedPath, scriptKind: ScriptKind, hasMixedContent: boolean, path: Path); isScriptOpen(): boolean; @@ -7409,14 +7409,14 @@ declare namespace ts.server { getSnapshot(): IScriptSnapshot; private ensureRealPath(); getFormatCodeSettings(): FormatCodeSettings; - getOptions(): Options; + getPreferences(): UserPreferences; attachToProject(project: Project): boolean; isAttached(project: Project): boolean; detachFromProject(project: Project): void; detachAllProjects(): void; getDefaultProject(): Project; registerFileUpdate(): void; - setOptions(formatSettings: FormatCodeSettings, options: Options): void; + setOptions(formatSettings: FormatCodeSettings, preferences: UserPreferences): void; getLatestVersion(): string; saveTo(fileName: string): void; reloadFromFile(tempFileName?: NormalizedPath): void; @@ -7794,7 +7794,7 @@ declare namespace ts.server { function convertScriptKindName(scriptKindName: protocol.ScriptKindName): ScriptKind.Unknown | ScriptKind.JS | ScriptKind.JSX | ScriptKind.TS | ScriptKind.TSX; interface HostConfiguration { formatCodeOptions: FormatCodeSettings; - options: Options; + preferences: UserPreferences; hostInfo: string; extraFileExtensions?: JsFileExtensionInfo[]; } @@ -7904,7 +7904,7 @@ declare namespace ts.server { private ensureProjectStructuresUptoDate(); private updateProjectIfDirty(project); getFormatCodeOptions(file: NormalizedPath): FormatCodeSettings; - getOptions(file: NormalizedPath): Options; + getPreferences(file: NormalizedPath): UserPreferences; private onSourceFileChanged(fileName, eventKind, path); private handleDeletedFile(info); private onConfigChangedForConfiguredProject(project, eventKind); diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index ccd5058127388..700341f793851 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -4319,11 +4319,11 @@ declare namespace ts { isKnownTypesPackageName?(name: string): boolean; installPackage?(options: InstallPackageOptions): Promise; } - interface Options { - readonly quote?: "double" | "single"; + interface UserPreferences { + readonly quotePreference?: "double" | "single"; readonly includeCompletionsForModuleExports?: boolean; readonly includeCompletionsWithInsertText?: boolean; - readonly importModuleSpecifierPreference?: "relative" | "baseUrl"; + readonly importModuleSpecifierPreference?: "relative" | "non-relative"; } interface LanguageService { cleanupSemanticCache(): void; @@ -4342,7 +4342,7 @@ declare namespace ts { getEncodedSyntacticClassifications(fileName: string, span: TextSpan): Classifications; getEncodedSemanticClassifications(fileName: string, span: TextSpan): Classifications; getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions | undefined): CompletionInfo; - getCompletionEntryDetails(fileName: string, position: number, name: string, formatOptions: FormatCodeOptions | FormatCodeSettings | undefined, source: string | undefined, options: Options | undefined): CompletionEntryDetails; + getCompletionEntryDetails(fileName: string, position: number, name: string, formatOptions: FormatCodeOptions | FormatCodeSettings | undefined, source: string | undefined, preferences: UserPreferences | undefined): CompletionEntryDetails; getCompletionEntrySymbol(fileName: string, position: number, name: string, source: string | undefined): Symbol; getQuickInfoAtPosition(fileName: string, position: number): QuickInfo; getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): TextSpan; @@ -4372,8 +4372,8 @@ declare namespace ts { getDocCommentTemplateAtPosition(fileName: string, position: number): TextInsertion; isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): boolean; getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan; - getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings, options: Options): ReadonlyArray; - getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, options: Options): CombinedCodeActions; + getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray, formatOptions: FormatCodeSettings, preferences: UserPreferences): ReadonlyArray; + getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, preferences: UserPreferences): CombinedCodeActions; applyCodeActionCommand(action: CodeActionCommand): Promise; applyCodeActionCommand(action: CodeActionCommand[]): Promise; applyCodeActionCommand(action: CodeActionCommand | CodeActionCommand[]): Promise; @@ -4383,9 +4383,9 @@ declare namespace ts { applyCodeActionCommand(fileName: string, action: CodeActionCommand[]): Promise; /** @deprecated `fileName` will be ignored */ applyCodeActionCommand(fileName: string, action: CodeActionCommand | CodeActionCommand[]): Promise; - getApplicableRefactors(fileName: string, positionOrRaneg: number | TextRange, options: Options | undefined): ApplicableRefactorInfo[]; - getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string, options: Options | undefined): RefactorEditInfo | undefined; - organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, options: Options | undefined): ReadonlyArray; + getApplicableRefactors(fileName: string, positionOrRaneg: number | TextRange, preferences: UserPreferences | undefined): ApplicableRefactorInfo[]; + getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string, preferences: UserPreferences | undefined): RefactorEditInfo | undefined; + organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): ReadonlyArray; getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean): EmitOutput; getProgram(): Program; dispose(): void; @@ -4395,8 +4395,8 @@ declare namespace ts { fileName: string; } type OrganizeImportsScope = CombinedCodeFixScope; - /** @deprecated Use Options */ - interface GetCompletionsAtPositionOptions extends Options { + /** @deprecated Use UserPreferences */ + interface GetCompletionsAtPositionOptions extends UserPreferences { /** @deprecated Use includeCompletionsForModuleExports */ includeExternalModuleExports?: boolean; /** @deprecated Use includeCompletionsWithInsertText */ diff --git a/tests/cases/fourslash/codeFixClassImplementInterface_optionQuote.ts b/tests/cases/fourslash/codeFixClassImplementInterface_optionQuote.ts index b480bb667299f..7fa6d8d9a9c97 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterface_optionQuote.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterface_optionQuote.ts @@ -16,5 +16,5 @@ class C implements I { throw new Error('Method not implemented.'); } }`, - options: { quote: "single" } + preferences: { quotePreference: "single" } }); diff --git a/tests/cases/fourslash/completionListInvalidMemberNames_escapeQuote.ts b/tests/cases/fourslash/completionListInvalidMemberNames_escapeQuote.ts index 7f196adb1223f..e61c9bc874a02 100644 --- a/tests/cases/fourslash/completionListInvalidMemberNames_escapeQuote.ts +++ b/tests/cases/fourslash/completionListInvalidMemberNames_escapeQuote.ts @@ -8,5 +8,5 @@ verify.completionsAt("", [{ name: `"'`, insertText: `["\\"'"]`, replacementSpan verify.completionsAt("", [{ name: `"'`, insertText: `['"\\'']`, replacementSpan }], { includeInsertTextCompletions: true, - quote: "single", + quotePreference: "single", }); diff --git a/tests/cases/fourslash/completionsImport_quoteStyle.ts b/tests/cases/fourslash/completionsImport_quoteStyle.ts index 86031a6aff892..cd3cc805daaa6 100644 --- a/tests/cases/fourslash/completionsImport_quoteStyle.ts +++ b/tests/cases/fourslash/completionsImport_quoteStyle.ts @@ -11,8 +11,8 @@ verify.applyCodeActionFromCompletion("", { name: "foo", source: "/a", description: `Import 'foo' from module "./a"`, - options: { - quote: "single", + preferences: { + quotePreference: "single", }, newFileContent: `import { foo } from './a'; diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 9f7add262a7de..ed239d64f2e38 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -152,7 +152,7 @@ declare namespace FourSlashInterface { kind?: string | { kind?: string, kindModifiers?: string }, spanIndex?: number, hasAction?: boolean, - options?: Options & { + options?: UserPreferences & { sourceDisplay?: string, isRecommended?: true, insertText?: string, @@ -179,7 +179,7 @@ declare namespace FourSlashInterface { newRangeContent?: string, errorCode?: number, index?: number, - options?: Options, + preferences?: UserPreferences, }); codeFixAvailable(options?: Array<{ description: string, actions?: Array<{ type: string, data: {} }>, commands?: {}[] }>): void; applicableRefactorAvailableAtMarker(markerName: string): void; @@ -211,7 +211,7 @@ declare namespace FourSlashInterface { description: string, newFileContent?: string, newRangeContent?: string, - options?: Options, + preferences?: UserPreferences, }); indentationIs(numberOfSpaces: number): void; indentationAtPositionIs(fileName: string, position: number, numberOfSpaces: number, indentStyle?: ts.IndentStyle, baseIndentSize?: number): void; @@ -299,7 +299,7 @@ declare namespace FourSlashInterface { rangeIs(expectedText: string, includeWhiteSpace?: boolean): void; fileAfterApplyingRefactorAtMarker(markerName: string, expectedContent: string, refactorNameToApply: string, formattingOptions?: FormatCodeOptions): void; getAndApplyCodeFix(errorCode?: number, index?: number): void; - importFixAtPosition(expectedTextArray: string[], errorCode?: number, options?: Options): void; + importFixAtPosition(expectedTextArray: string[], errorCode?: number, options?: UserPreferences): void; navigationBar(json: any, options?: { checkSpans?: boolean }): void; navigationTree(json: any, options?: { checkSpans?: boolean }): void; @@ -525,13 +525,13 @@ declare namespace FourSlashInterface { range?: Range; code: number; } - interface Options { - quote?: "double" | "single"; + interface UserPreferences { + quotePreference?: "double" | "single"; includeCompletionsForModuleExports?: boolean; includeInsertTextCompletions?: boolean; - importModuleSpecifierPreference?: "relative" | "baseUrl"; + importModuleSpecifierPreference?: "relative" | "non-relative"; } - interface CompletionsAtOptions extends Options { + interface CompletionsAtOptions extends UserPreferences { isNewIdentifierLocation?: boolean; } } diff --git a/tests/cases/fourslash/importNameCodeFixNewImportBaseUrl1.ts b/tests/cases/fourslash/importNameCodeFixNewImportBaseUrl1.ts index d34193b9a6082..fa28dea5fe233 100644 --- a/tests/cases/fourslash/importNameCodeFixNewImportBaseUrl1.ts +++ b/tests/cases/fourslash/importNameCodeFixNewImportBaseUrl1.ts @@ -29,5 +29,5 @@ verify.importFixAtPosition([ f1();`, ], /*errorCode*/ undefined, { - importModuleSpecifierPreference: "baseUrl", + importModuleSpecifierPreference: "non-relative", }); diff --git a/tests/cases/fourslash/importNameCodeFix_quoteStyle.ts b/tests/cases/fourslash/importNameCodeFix_quoteStyle.ts index 3e16a39252993..b7479dea3e85c 100644 --- a/tests/cases/fourslash/importNameCodeFix_quoteStyle.ts +++ b/tests/cases/fourslash/importNameCodeFix_quoteStyle.ts @@ -12,5 +12,5 @@ verify.importFixAtPosition([ foo;`, ], /*errorCode*/ undefined, { - quote: "single", + quotePreference: "single", });