From c533b0ea2987bcfc503d478250d467be56828c31 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 13 Jul 2017 08:20:54 -0700 Subject: [PATCH 1/3] Session: don't return undefined if a response is required --- src/server/session.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/server/session.ts b/src/server/session.ts index cdd4306c064da..2cdec33e75835 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -606,7 +606,7 @@ namespace ts.server { const definitions = project.getLanguageService().getDefinitionAtPosition(file, position); if (!definitions) { - return undefined; + return []; } if (simplifiedResult) { @@ -674,7 +674,7 @@ namespace ts.server { const occurrences = project.getLanguageService().getOccurrencesAtPosition(file, position); if (!occurrences) { - return undefined; + return []; } return occurrences.map(occurrence => { @@ -917,7 +917,7 @@ namespace ts.server { if (simplifiedResult) { const nameInfo = defaultProject.getLanguageService().getQuickInfoAtPosition(file, position); if (!nameInfo) { - return undefined; + return []; } const displayString = ts.displayPartsToString(nameInfo.displayParts); @@ -995,7 +995,7 @@ namespace ts.server { return args.position !== undefined ? args.position : scriptInfo.lineOffsetToPosition(args.line, args.offset); } - private getFileAndProject(args: protocol.FileRequestArgs, errorOnMissingProject = true) { + private getFileAndProject(args: protocol.FileRequestArgs, errorOnMissingProject = true): { file: NormalizedPath, project: Project } { return this.getFileAndProjectWorker(args.file, args.projectFileName, /*refreshInferredProjects*/ true, errorOnMissingProject); } @@ -1180,7 +1180,7 @@ namespace ts.server { const completions = project.getLanguageService().getCompletionsAtPosition(file, position); if (!completions) { - return undefined; + return []; } if (simplifiedResult) { return completions.entries.reduce((result: protocol.CompletionEntry[], entry: ts.CompletionEntry) => { From a31ad7843653dd15e868a355d3e1d3706b90ef6f Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 8 Aug 2017 07:52:11 -0700 Subject: [PATCH 2/3] Use ReadonlyArray and emptyArray --- src/harness/unittests/projectErrors.ts | 4 +- src/server/editorServices.ts | 18 ++++---- src/server/project.ts | 8 ++-- src/server/protocol.ts | 2 +- src/server/session.ts | 62 +++++++++++++------------- 5 files changed, 48 insertions(+), 46 deletions(-) diff --git a/src/harness/unittests/projectErrors.ts b/src/harness/unittests/projectErrors.ts index f250d4d9b36f4..4a76ed2c98c76 100644 --- a/src/harness/unittests/projectErrors.ts +++ b/src/harness/unittests/projectErrors.ts @@ -4,12 +4,12 @@ namespace ts.projectSystem { describe("Project errors", () => { - function checkProjectErrors(projectFiles: server.ProjectFilesWithTSDiagnostics, expectedErrors: string[]) { + function checkProjectErrors(projectFiles: server.ProjectFilesWithTSDiagnostics, expectedErrors: ReadonlyArray): void { assert.isTrue(projectFiles !== undefined, "missing project files"); checkProjectErrorsWorker(projectFiles.projectErrors, expectedErrors); } - function checkProjectErrorsWorker(errors: Diagnostic[], expectedErrors: string[]) { + function checkProjectErrorsWorker(errors: ReadonlyArray, expectedErrors: ReadonlyArray): void { assert.equal(errors ? errors.length : 0, expectedErrors.length, `expected ${expectedErrors.length} error in the list`); if (expectedErrors.length) { for (let i = 0; i < errors.length; i++) { diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 4ffdf5d6c5d99..346a65df6a099 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -22,7 +22,7 @@ namespace ts.server { export interface ConfigFileDiagEvent { eventName: typeof ConfigFileDiagEvent; - data: { triggerFile: string, configFileName: string, diagnostics: Diagnostic[] }; + data: { triggerFile: string, configFileName: string, diagnostics: ReadonlyArray }; } export interface ProjectLanguageServiceStateEvent { @@ -200,7 +200,7 @@ namespace ts.server { /** * This helper function processes a list of projects and return the concatenated, sortd and deduplicated output of processing each project. */ - export function combineProjectOutput(projects: Project[], action: (project: Project) => T[], comparer?: (a: T, b: T) => number, areEqual?: (a: T, b: T) => boolean) { + export function combineProjectOutput(projects: ReadonlyArray, action: (project: Project) => ReadonlyArray, comparer?: (a: T, b: T) => number, areEqual?: (a: T, b: T) => boolean) { const result = flatMap(projects, action).sort(comparer); return projects.length > 1 ? deduplicate(result, areEqual) : result; } @@ -220,14 +220,14 @@ namespace ts.server { interface OpenConfigFileResult { success: boolean; - errors?: Diagnostic[]; + errors?: ReadonlyArray; project?: ConfiguredProject; } export interface OpenConfiguredProjectResult { configFileName?: NormalizedPath; - configFileErrors?: Diagnostic[]; + configFileErrors?: ReadonlyArray; } interface FilePropertyReader { @@ -1102,18 +1102,18 @@ namespace ts.server { } } - private reportConfigFileDiagnostics(configFileName: string, diagnostics: Diagnostic[], triggerFile: string) { + private reportConfigFileDiagnostics(configFileName: string, diagnostics: ReadonlyArray, triggerFile: string) { if (!this.eventHandler) { return; } this.eventHandler({ eventName: ConfigFileDiagEvent, - data: { configFileName, diagnostics: diagnostics || [], triggerFile } + data: { configFileName, diagnostics: diagnostics || emptyArray, triggerFile } }); } - private createAndAddConfiguredProject(configFileName: NormalizedPath, projectOptions: ProjectOptions, configFileErrors: Diagnostic[], clientFileName?: string) { + private createAndAddConfiguredProject(configFileName: NormalizedPath, projectOptions: ProjectOptions, configFileErrors: ReadonlyArray, clientFileName?: string) { const sizeLimitExceeded = this.exceededTotalSizeLimitForNonTsFiles(configFileName, projectOptions.compilerOptions, projectOptions.files, fileNamePropertyReader); const project = new ConfiguredProject( configFileName, @@ -1145,7 +1145,7 @@ namespace ts.server { } } - private addFilesToProjectAndUpdateGraph(project: ConfiguredProject | ExternalProject, files: T[], propertyReader: FilePropertyReader, clientFileName: string, typeAcquisition: TypeAcquisition, configFileErrors: Diagnostic[]): void { + private addFilesToProjectAndUpdateGraph(project: ConfiguredProject | ExternalProject, files: T[], propertyReader: FilePropertyReader, clientFileName: string, typeAcquisition: TypeAcquisition, configFileErrors: ReadonlyArray): void { let errors: Diagnostic[]; for (const f of files) { const rootFilename = propertyReader.getFileName(f); @@ -1458,7 +1458,7 @@ namespace ts.server { openClientFileWithNormalizedPath(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, projectRootPath?: NormalizedPath): OpenConfiguredProjectResult { let configFileName: NormalizedPath; - let configFileErrors: Diagnostic[]; + let configFileErrors: ReadonlyArray; let project: ConfiguredProject | ExternalProject = this.findContainingExternalProject(fileName); if (!project) { diff --git a/src/server/project.ts b/src/server/project.ts index 524b6c4d28d3d..fbd6dcb205711 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -54,7 +54,7 @@ namespace ts.server { /* @internal */ export interface ProjectFilesWithTSDiagnostics extends protocol.ProjectFiles { - projectErrors: Diagnostic[]; + projectErrors: ReadonlyArray; } export class UnresolvedImportsMap { @@ -147,7 +147,7 @@ namespace ts.server { private typingFiles: SortedReadonlyArray; - protected projectErrors: Diagnostic[]; + protected projectErrors: ReadonlyArray; public typesVersion = 0; @@ -1044,7 +1044,7 @@ namespace ts.server { return getDirectoryPath(this.getConfigFilePath()); } - setProjectErrors(projectErrors: Diagnostic[]) { + setProjectErrors(projectErrors: ReadonlyArray) { this.projectErrors = projectErrors; } @@ -1188,7 +1188,7 @@ namespace ts.server { return this.typeAcquisition; } - setProjectErrors(projectErrors: Diagnostic[]) { + setProjectErrors(projectErrors: ReadonlyArray) { this.projectErrors = projectErrors; } diff --git a/src/server/protocol.ts b/src/server/protocol.ts index b6756a7d4ff11..a75c04764c036 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -914,7 +914,7 @@ namespace ts.server.protocol { /** * An array of span groups (one per file) that refer to the item to be renamed. */ - locs: SpanGroup[]; + locs: ReadonlyArray; } /** diff --git a/src/server/session.ts b/src/server/session.ts index a03cdfce63232..5f29793800a0e 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -383,7 +383,7 @@ namespace ts.server { this.host.write(formatMessage(msg, this.logger, this.byteLength, this.host.newLine)); } - public configFileDiagnosticEvent(triggerFile: string, configFile: string, diagnostics: Diagnostic[]) { + public configFileDiagnosticEvent(triggerFile: string, configFile: string, diagnostics: ReadonlyArray) { const bakedDiags = map(diagnostics, diagnostic => formatConfigFileDiag(diagnostic, /*includeFileName*/ true)); const ev: protocol.ConfigFileDiagnosticEvent = { seq: 0, @@ -543,7 +543,7 @@ namespace ts.server { ); } - private convertToDiagnosticsWithLinePositionFromDiagnosticFile(diagnostics: Diagnostic[]) { + private convertToDiagnosticsWithLinePositionFromDiagnosticFile(diagnostics: ReadonlyArray): protocol.DiagnosticWithLinePosition[] { return diagnostics.map(d => { message: flattenDiagnosticMessageText(d.messageText, this.host.newLine), start: d.start, @@ -569,7 +569,7 @@ namespace ts.server { ); } - private convertToDiagnosticsWithLinePosition(diagnostics: Diagnostic[], scriptInfo: ScriptInfo) { + private convertToDiagnosticsWithLinePosition(diagnostics: ReadonlyArray, scriptInfo: ScriptInfo): protocol.DiagnosticWithLinePosition[] { return diagnostics.map(d => { message: flattenDiagnosticMessageText(d.messageText, this.host.newLine), start: d.start, @@ -582,10 +582,12 @@ namespace ts.server { }); } - private getDiagnosticsWorker(args: protocol.FileRequestArgs, isSemantic: boolean, selector: (project: Project, file: string) => Diagnostic[], includeLinePosition: boolean) { + private getDiagnosticsWorker( + args: protocol.FileRequestArgs, isSemantic: boolean, selector: (project: Project, file: string) => ReadonlyArray, includeLinePosition: boolean + ): ReadonlyArray | ReadonlyArray { const { project, file } = this.getFileAndProject(args); if (isSemantic && isDeclarationFileInJSOnlyNonConfiguredProject(project, file)) { - return []; + return emptyArray; } const scriptInfo = project.getScriptInfoForNormalizedPath(file); const diagnostics = selector(project, file); @@ -594,14 +596,14 @@ namespace ts.server { : diagnostics.map(d => formatDiag(file, project, d)); } - private getDefinition(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): protocol.FileSpan[] | DefinitionInfo[] { + private getDefinition(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): ReadonlyArray | ReadonlyArray { const { file, project } = this.getFileAndProject(args); const scriptInfo = project.getScriptInfoForNormalizedPath(file); const position = this.getPosition(args, scriptInfo); const definitions = project.getLanguageService().getDefinitionAtPosition(file, position); if (!definitions) { - return []; + return emptyArray; } if (simplifiedResult) { @@ -619,7 +621,7 @@ namespace ts.server { } } - private getTypeDefinition(args: protocol.FileLocationRequestArgs): protocol.FileSpan[] { + private getTypeDefinition(args: protocol.FileLocationRequestArgs): ReadonlyArray { const { file, project } = this.getFileAndProject(args); const scriptInfo = project.getScriptInfoForNormalizedPath(file); const position = this.getPosition(args, scriptInfo); @@ -639,12 +641,12 @@ namespace ts.server { }); } - private getImplementation(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): protocol.FileSpan[] | ImplementationLocation[] { + private getImplementation(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): ReadonlyArray | ReadonlyArray { const { file, project } = this.getFileAndProject(args); const position = this.getPosition(args, project.getScriptInfoForNormalizedPath(file)); const implementations = project.getLanguageService().getImplementationAtPosition(file, position); if (!implementations) { - return []; + return emptyArray; } if (simplifiedResult) { return implementations.map(({ fileName, textSpan }) => { @@ -661,7 +663,7 @@ namespace ts.server { } } - private getOccurrences(args: protocol.FileLocationRequestArgs): protocol.OccurrencesResponseItem[] { + private getOccurrences(args: protocol.FileLocationRequestArgs): ReadonlyArray { const { file, project } = this.getFileAndProject(args); const scriptInfo = project.getScriptInfoForNormalizedPath(file); const position = this.getPosition(args, scriptInfo); @@ -669,7 +671,7 @@ namespace ts.server { const occurrences = project.getLanguageService().getOccurrencesAtPosition(file, position); if (!occurrences) { - return []; + return emptyArray; } return occurrences.map(occurrence => { @@ -691,17 +693,17 @@ namespace ts.server { }); } - private getSyntacticDiagnosticsSync(args: protocol.SyntacticDiagnosticsSyncRequestArgs): protocol.Diagnostic[] | protocol.DiagnosticWithLinePosition[] { + private getSyntacticDiagnosticsSync(args: protocol.SyntacticDiagnosticsSyncRequestArgs): ReadonlyArray | ReadonlyArray { const { configFile } = this.getConfigFileAndProject(args); if (configFile) { // all the config file errors are reported as part of semantic check so nothing to report here - return []; + return emptyArray; } return this.getDiagnosticsWorker(args, /*isSemantic*/ false, (project, file) => project.getLanguageService().getSyntacticDiagnostics(file), args.includeLinePosition); } - private getSemanticDiagnosticsSync(args: protocol.SemanticDiagnosticsSyncRequestArgs): protocol.Diagnostic[] | protocol.DiagnosticWithLinePosition[] { + private getSemanticDiagnosticsSync(args: protocol.SemanticDiagnosticsSyncRequestArgs): ReadonlyArray | ReadonlyArray { const { configFile, project } = this.getConfigFileAndProject(args); if (configFile) { return this.getConfigFileDiagnostics(configFile, project, args.includeLinePosition); @@ -709,7 +711,7 @@ namespace ts.server { return this.getDiagnosticsWorker(args, /*isSemantic*/ true, (project, file) => project.getLanguageService().getSemanticDiagnostics(file), args.includeLinePosition); } - private getDocumentHighlights(args: protocol.DocumentHighlightsRequestArgs, simplifiedResult: boolean): protocol.DocumentHighlightsItem[] | DocumentHighlights[] { + private getDocumentHighlights(args: protocol.DocumentHighlightsRequestArgs, simplifiedResult: boolean): ReadonlyArray | ReadonlyArray { const { file, project } = this.getFileAndProject(args); const scriptInfo = project.getScriptInfoForNormalizedPath(file); const position = this.getPosition(args, scriptInfo); @@ -800,7 +802,7 @@ namespace ts.server { return info.getDefaultProject(); } - private getRenameLocations(args: protocol.RenameRequestArgs, simplifiedResult: boolean): protocol.RenameResponseBody | RenameLocation[] { + private getRenameLocations(args: protocol.RenameRequestArgs, simplifiedResult: boolean): protocol.RenameResponseBody | ReadonlyArray { const file = toNormalizedPath(args.file); const info = this.projectService.getScriptInfoForNormalizedPath(file); const position = this.getPosition(args, info); @@ -817,7 +819,7 @@ namespace ts.server { if (!renameInfo.canRename) { return { info: renameInfo, - locs: [] + locs: emptyArray }; } @@ -826,7 +828,7 @@ namespace ts.server { (project: Project) => { const renameLocations = project.getLanguageService().findRenameLocations(file, position, args.findInStrings, args.findInComments); if (!renameLocations) { - return []; + return emptyArray; } return renameLocations.map(location => { @@ -903,7 +905,7 @@ namespace ts.server { } } - private getReferences(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): protocol.ReferencesResponseBody | ReferencedSymbol[] { + private getReferences(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): protocol.ReferencesResponseBody | ReadonlyArray { const file = toNormalizedPath(args.file); const projects = this.getProjects(args); @@ -913,7 +915,7 @@ namespace ts.server { if (simplifiedResult) { const nameInfo = defaultProject.getLanguageService().getQuickInfoAtPosition(file, position); if (!nameInfo) { - return []; + return emptyArray; } const displayString = displayPartsToString(nameInfo.displayParts); @@ -925,7 +927,7 @@ namespace ts.server { (project: Project) => { const references = project.getLanguageService().getReferencesAtPosition(file, position); if (!references) { - return []; + return emptyArray; } return references.map(ref => { @@ -982,7 +984,7 @@ namespace ts.server { if (this.eventHandler) { this.eventHandler({ eventName: "configFileDiag", - data: { triggerFile: fileName, configFileName, diagnostics: configFileErrors || [] } + data: { triggerFile: fileName, configFileName, diagnostics: configFileErrors || emptyArray } }); } } @@ -1167,7 +1169,7 @@ namespace ts.server { }); } - private getCompletions(args: protocol.CompletionsRequestArgs, simplifiedResult: boolean): protocol.CompletionEntry[] | CompletionInfo { + private getCompletions(args: protocol.CompletionsRequestArgs, simplifiedResult: boolean): ReadonlyArray | CompletionInfo { const prefix = args.prefix || ""; const { file, project } = this.getFileAndProject(args); @@ -1176,7 +1178,7 @@ namespace ts.server { const completions = project.getLanguageService().getCompletionsAtPosition(file, position); if (!completions) { - return []; + return emptyArray; } if (simplifiedResult) { return mapDefined(completions.entries, entry => { @@ -1192,7 +1194,7 @@ namespace ts.server { } } - private getCompletionEntryDetails(args: protocol.CompletionDetailsRequestArgs): protocol.CompletionEntryDetails[] { + private getCompletionEntryDetails(args: protocol.CompletionDetailsRequestArgs): ReadonlyArray { const { file, project } = this.getFileAndProject(args); const scriptInfo = project.getScriptInfoForNormalizedPath(file); const position = this.getPosition(args, scriptInfo); @@ -1201,12 +1203,12 @@ namespace ts.server { project.getLanguageService().getCompletionEntryDetails(file, position, entryName)); } - private getCompileOnSaveAffectedFileList(args: protocol.FileRequestArgs): protocol.CompileOnSaveAffectedFileListSingleProject[] { + private getCompileOnSaveAffectedFileList(args: protocol.FileRequestArgs): ReadonlyArray { const info = this.projectService.getScriptInfo(args.file); const result: protocol.CompileOnSaveAffectedFileListSingleProject[] = []; if (!info) { - return []; + return emptyArray; } // if specified a project, we only return affected file list in this project @@ -1364,7 +1366,7 @@ namespace ts.server { : tree; } - private getNavigateToItems(args: protocol.NavtoRequestArgs, simplifiedResult: boolean): protocol.NavtoItem[] | NavigateToItem[] { + private getNavigateToItems(args: protocol.NavtoRequestArgs, simplifiedResult: boolean): ReadonlyArray | ReadonlyArray { const projects = this.getProjects(args); const fileName = args.currentFileOnly ? args.file && normalizeSlashes(args.file) : undefined; @@ -1374,7 +1376,7 @@ namespace ts.server { project => { const navItems = project.getLanguageService().getNavigateToItems(args.searchValue, args.maxResultCount, fileName, /*excludeDts*/ project.isNonTsProject()); if (!navItems) { - return []; + return emptyArray; } return navItems.map((navItem) => { From 8931ddc77082c14f4f5b074dfdae3d14063b641e Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 8 Aug 2017 07:53:23 -0700 Subject: [PATCH 3/3] Remove inferred return type --- src/server/session.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/session.ts b/src/server/session.ts index 5f29793800a0e..f9c7241a4cd4e 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -993,7 +993,7 @@ namespace ts.server { return args.position !== undefined ? args.position : scriptInfo.lineOffsetToPosition(args.line, args.offset); } - private getFileAndProject(args: protocol.FileRequestArgs, errorOnMissingProject = true): { file: NormalizedPath, project: Project } { + private getFileAndProject(args: protocol.FileRequestArgs, errorOnMissingProject = true) { return this.getFileAndProjectWorker(args.file, args.projectFileName, /*refreshInferredProjects*/ true, errorOnMissingProject); }