From e8fd1cf4668f8f45e5d007b8050a1eddd65f5bb9 Mon Sep 17 00:00:00 2001 From: Zhengbo Li Date: Wed, 19 Apr 2017 11:42:24 -0700 Subject: [PATCH] Support project root path for controlling config file searching depth (#15238) * stops at projectRootPath when searching config file * Add tests * Update editorServices.ts Remove extra `true &&` --- .../unittests/tsserverProjectSystem.ts | 24 +++++++++++++++++++ src/server/editorServices.ts | 16 ++++++------- src/server/protocol.ts | 5 ++++ src/server/session.ts | 10 +++++--- 4 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 66d9cb087b6..321ca9e3e0c 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -3493,6 +3493,30 @@ namespace ts.projectSystem { }); }); + describe("searching for config file", () => { + it("should stop at projectRootPath if given", () => { + const f1 = { + path: "/a/file1.ts", + content: "" + }; + const configFile = { + path: "/tsconfig.json", + content: "{}" + }; + const host = createServerHost([f1, configFile]); + const service = createProjectService(host); + service.openClientFile(f1.path, /*fileContent*/ undefined, /*scriptKind*/ undefined, "/a"); + + checkNumberOfConfiguredProjects(service, 0); + checkNumberOfInferredProjects(service, 1); + + service.closeClientFile(f1.path); + service.openClientFile(f1.path); + checkNumberOfConfiguredProjects(service, 1); + checkNumberOfInferredProjects(service, 0); + }); + }); + describe("cancellationToken", () => { it("is attached to request", () => { const f1 = { diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index d45bb283384..4699e1a1d4d 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -787,12 +787,12 @@ namespace ts.server { * we first detect if there is already a configured project created for it: if so, we re-read * the tsconfig file content and update the project; otherwise we create a new one. */ - private openOrUpdateConfiguredProjectForFile(fileName: NormalizedPath): OpenConfiguredProjectResult { + private openOrUpdateConfiguredProjectForFile(fileName: NormalizedPath, projectRootPath?: NormalizedPath): OpenConfiguredProjectResult { const searchPath = getDirectoryPath(fileName); this.logger.info(`Search path: ${searchPath}`); // check if this file is already included in one of external projects - const configFileName = this.findConfigFile(asNormalizedPath(searchPath)); + const configFileName = this.findConfigFile(asNormalizedPath(searchPath), projectRootPath); if (!configFileName) { this.logger.info("No config files found."); return {}; @@ -826,8 +826,8 @@ namespace ts.server { // current directory (the directory in which tsc was invoked). // The server must start searching from the directory containing // the newly opened file. - private findConfigFile(searchPath: NormalizedPath): NormalizedPath { - while (true) { + private findConfigFile(searchPath: NormalizedPath, projectRootPath?: NormalizedPath): NormalizedPath { + while (!projectRootPath || searchPath.indexOf(projectRootPath) >= 0) { const tsconfigFileName = asNormalizedPath(combinePaths(searchPath, "tsconfig.json")); if (this.host.fileExists(tsconfigFileName)) { return tsconfigFileName; @@ -1326,17 +1326,17 @@ namespace ts.server { * @param filename is absolute pathname * @param fileContent is a known version of the file content that is more up to date than the one on disk */ - openClientFile(fileName: string, fileContent?: string, scriptKind?: ScriptKind): OpenConfiguredProjectResult { - return this.openClientFileWithNormalizedPath(toNormalizedPath(fileName), fileContent, scriptKind); + openClientFile(fileName: string, fileContent?: string, scriptKind?: ScriptKind, projectRootPath?: string): OpenConfiguredProjectResult { + return this.openClientFileWithNormalizedPath(toNormalizedPath(fileName), fileContent, scriptKind, /*hasMixedContent*/ false, projectRootPath ? toNormalizedPath(projectRootPath) : undefined); } - openClientFileWithNormalizedPath(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean): OpenConfiguredProjectResult { + openClientFileWithNormalizedPath(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, projectRootPath?: NormalizedPath): OpenConfiguredProjectResult { let configFileName: NormalizedPath; let configFileErrors: Diagnostic[]; let project: ConfiguredProject | ExternalProject = this.findContainingExternalProject(fileName); if (!project) { - ({ configFileName, configFileErrors } = this.openOrUpdateConfiguredProjectForFile(fileName)); + ({ configFileName, configFileErrors } = this.openOrUpdateConfiguredProjectForFile(fileName, projectRootPath)); if (configFileName) { project = this.findConfiguredProjectByProjectName(configFileName); } diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 71998666fed..8e3c6c2c07f 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -1041,6 +1041,11 @@ namespace ts.server.protocol { * "TS", "JS", "TSX", "JSX" */ scriptKindName?: ScriptKindName; + /** + * Used to limit the searching for project config file. If given the searching will stop at this + * root path; otherwise it will go all the way up to the dist root path. + */ + projectRootPath?: string; } export type ScriptKindName = "TS" | "JS" | "TSX" | "JSX"; diff --git a/src/server/session.ts b/src/server/session.ts index 742f8a3c228..194c6f667bb 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -979,8 +979,8 @@ namespace ts.server { * @param fileName is the name of the file to be opened * @param fileContent is a version of the file content that is known to be more up to date than the one on disk */ - private openClientFile(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind) { - const { configFileName, configFileErrors } = this.projectService.openClientFileWithNormalizedPath(fileName, fileContent, scriptKind); + private openClientFile(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind, projectRootPath?: NormalizedPath) { + const { configFileName, configFileErrors } = this.projectService.openClientFileWithNormalizedPath(fileName, fileContent, scriptKind, /*hasMixedContent*/ false, projectRootPath); if (this.eventHandler) { this.eventHandler({ eventName: "configFileDiag", @@ -1659,7 +1659,11 @@ namespace ts.server { return this.requiredResponse(this.getRenameInfo(request.arguments)); }, [CommandNames.Open]: (request: protocol.OpenRequest) => { - this.openClientFile(toNormalizedPath(request.arguments.file), request.arguments.fileContent, convertScriptKindName(request.arguments.scriptKindName)); + this.openClientFile( + toNormalizedPath(request.arguments.file), + request.arguments.fileContent, + convertScriptKindName(request.arguments.scriptKindName), + request.arguments.projectRootPath ? toNormalizedPath(request.arguments.projectRootPath) : undefined); return this.notRequired(); }, [CommandNames.Quickinfo]: (request: protocol.QuickInfoRequest) => {