From 5ac450510a783045b337cf07d0dc7bb2d529fc54 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 20 Aug 2019 10:52:56 -0700 Subject: [PATCH] Handle network style paths for watching (#32888) * Refactoring * take windows style root as test server host parameter * Handle network style paths for watching Fixes #32796 --- src/compiler/resolutionCache.ts | 22 +++++-- src/harness/virtualFileSystemWithWatch.ts | 65 ++++++++++--------- src/testRunner/unittests/tsserver/projects.ts | 2 +- .../unittests/tsserver/watchEnvironment.ts | 37 ++++++++++- 4 files changed, 89 insertions(+), 37 deletions(-) diff --git a/src/compiler/resolutionCache.ts b/src/compiler/resolutionCache.ts index 13e6b9f280e..9bac99e331b 100644 --- a/src/compiler/resolutionCache.ts +++ b/src/compiler/resolutionCache.ts @@ -90,14 +90,28 @@ namespace ts { return false; } - const nextDirectorySeparator = dirPath.indexOf(directorySeparator, rootLength); + let nextDirectorySeparator = dirPath.indexOf(directorySeparator, rootLength); if (nextDirectorySeparator === -1) { // ignore "/user", "c:/users" or "c:/folderAtRoot" return false; } - if (dirPath.charCodeAt(0) !== CharacterCodes.slash && - dirPath.substr(rootLength, nextDirectorySeparator).search(/users/i) === -1) { + let pathPartForUserCheck = dirPath.substring(rootLength, nextDirectorySeparator + 1); + const isNonDirectorySeparatorRoot = rootLength > 1 || dirPath.charCodeAt(0) !== CharacterCodes.slash; + if (isNonDirectorySeparatorRoot && + dirPath.search(/[a-zA-Z]:/) !== 0 && // Non dos style paths + pathPartForUserCheck.search(/[a-zA-z]\$\//) === 0) { // Dos style nextPart + nextDirectorySeparator = dirPath.indexOf(directorySeparator, nextDirectorySeparator + 1); + if (nextDirectorySeparator === -1) { + // ignore "//vda1cs4850/c$/folderAtRoot" + return false; + } + + pathPartForUserCheck = dirPath.substring(rootLength + pathPartForUserCheck.length, nextDirectorySeparator + 1); + } + + if (isNonDirectorySeparatorRoot && + pathPartForUserCheck.search(/users\//i) !== 0) { // Paths like c:/folderAtRoot/subFolder are allowed return true; } @@ -105,7 +119,7 @@ namespace ts { for (let searchIndex = nextDirectorySeparator + 1, searchLevels = 2; searchLevels > 0; searchLevels--) { searchIndex = dirPath.indexOf(directorySeparator, searchIndex) + 1; if (searchIndex === 0) { - // Folder isnt at expected minimun levels + // Folder isnt at expected minimum levels return false; } } diff --git a/src/harness/virtualFileSystemWithWatch.ts b/src/harness/virtualFileSystemWithWatch.ts index ffb1e19f653..5c0ead21f08 100644 --- a/src/harness/virtualFileSystemWithWatch.ts +++ b/src/harness/virtualFileSystemWithWatch.ts @@ -35,38 +35,16 @@ interface Array { length: number; [n: number]: T; }` executingFilePath?: string; currentDirectory?: string; newLine?: string; - useWindowsStylePaths?: boolean; + windowsStyleRoot?: string; environmentVariables?: Map; } export function createWatchedSystem(fileOrFolderList: ReadonlyArray, params?: TestServerHostCreationParameters): TestServerHost { - if (!params) { - params = {}; - } - const host = new TestServerHost(/*withSafelist*/ false, - params.useCaseSensitiveFileNames !== undefined ? params.useCaseSensitiveFileNames : false, - params.executingFilePath || getExecutingFilePathFromLibFile(), - params.currentDirectory || "/", - fileOrFolderList, - params.newLine, - params.useWindowsStylePaths, - params.environmentVariables); - return host; + return new TestServerHost(/*withSafelist*/ false, fileOrFolderList, params); } export function createServerHost(fileOrFolderList: ReadonlyArray, params?: TestServerHostCreationParameters): TestServerHost { - if (!params) { - params = {}; - } - const host = new TestServerHost(/*withSafelist*/ true, - params.useCaseSensitiveFileNames !== undefined ? params.useCaseSensitiveFileNames : false, - params.executingFilePath || getExecutingFilePathFromLibFile(), - params.currentDirectory || "/", - fileOrFolderList, - params.newLine, - params.useWindowsStylePaths, - params.environmentVariables); - return host; + return new TestServerHost(/*withSafelist*/ true, fileOrFolderList, params); } export interface File { @@ -326,6 +304,16 @@ interface Array { length: number; [n: number]: T; }` } const timeIncrements = 1000; + export interface TestServerHostOptions { + useCaseSensitiveFileNames: boolean; + executingFilePath: string; + currentDirectory: string; + fileOrFolderorSymLinkList: ReadonlyArray; + newLine?: string; + useWindowsStylePaths?: boolean; + environmentVariables?: Map; + } + export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost, ModuleResolutionHost { args: string[] = []; @@ -342,16 +330,31 @@ interface Array { length: number; [n: number]: T; }` readonly watchedDirectories = createMultiMap(); readonly watchedDirectoriesRecursive = createMultiMap(); readonly watchedFiles = createMultiMap(); + public readonly useCaseSensitiveFileNames: boolean; + public readonly newLine: string; + public readonly windowsStyleRoot?: string; + private readonly environmentVariables?: Map; private readonly executingFilePath: string; private readonly currentDirectory: string; private readonly customWatchFile: HostWatchFile | undefined; private readonly customRecursiveWatchDirectory: HostWatchDirectory | undefined; public require: ((initialPath: string, moduleName: string) => server.RequireResult) | undefined; - constructor(public withSafeList: boolean, public useCaseSensitiveFileNames: boolean, executingFilePath: string, currentDirectory: string, fileOrFolderorSymLinkList: ReadonlyArray, public readonly newLine = "\n", public readonly useWindowsStylePath?: boolean, private readonly environmentVariables?: Map) { - this.getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames); + constructor( + public withSafeList: boolean, + fileOrFolderorSymLinkList: ReadonlyArray, + { + useCaseSensitiveFileNames, executingFilePath, currentDirectory, + newLine, windowsStyleRoot, environmentVariables + }: TestServerHostCreationParameters = {}) { + this.useCaseSensitiveFileNames = !!useCaseSensitiveFileNames; + this.newLine = newLine || "\n"; + this.windowsStyleRoot = windowsStyleRoot; + this.environmentVariables = environmentVariables; + currentDirectory = currentDirectory || "/"; + this.getCanonicalFileName = createGetCanonicalFileName(!!useCaseSensitiveFileNames); this.toPath = s => toPath(s, currentDirectory, this.getCanonicalFileName); - this.executingFilePath = this.getHostSpecificPath(executingFilePath); + this.executingFilePath = this.getHostSpecificPath(executingFilePath || getExecutingFilePathFromLibFile()); this.currentDirectory = this.getHostSpecificPath(currentDirectory); this.reloadFS(fileOrFolderorSymLinkList); const tscWatchFile = this.environmentVariables && this.environmentVariables.get("TSC_WATCHFILE") as Tsc_WatchFile; @@ -418,8 +421,8 @@ interface Array { length: number; [n: number]: T; }` } getHostSpecificPath(s: string) { - if (this.useWindowsStylePath && s.startsWith(directorySeparator)) { - return "c:/" + s.substring(1); + if (this.windowsStyleRoot && s.startsWith(directorySeparator)) { + return this.windowsStyleRoot + s.substring(1); } return s; } @@ -433,7 +436,7 @@ interface Array { length: number; [n: number]: T; }` const mapNewLeaves = createMap(); const isNewFs = this.fs.size === 0; fileOrFolderOrSymLinkList = fileOrFolderOrSymLinkList.concat(this.withSafeList ? safeList : []); - const filesOrFoldersToLoad: ReadonlyArray = !this.useWindowsStylePath ? fileOrFolderOrSymLinkList : + const filesOrFoldersToLoad: ReadonlyArray = !this.windowsStyleRoot ? fileOrFolderOrSymLinkList : fileOrFolderOrSymLinkList.map(f => { const result = clone(f); result.path = this.getHostSpecificPath(f.path); diff --git a/src/testRunner/unittests/tsserver/projects.ts b/src/testRunner/unittests/tsserver/projects.ts index 78d22615256..2d510fb4f8e 100644 --- a/src/testRunner/unittests/tsserver/projects.ts +++ b/src/testRunner/unittests/tsserver/projects.ts @@ -1061,7 +1061,7 @@ namespace ts.projectSystem { content: "let x = 1;" }; - const host = createServerHost([file1, configFile], { useWindowsStylePaths: true }); + const host = createServerHost([file1, configFile], { windowsStyleRoot: "c:/" }); const projectService = createProjectService(host); projectService.openClientFile(file1.path); diff --git a/src/testRunner/unittests/tsserver/watchEnvironment.ts b/src/testRunner/unittests/tsserver/watchEnvironment.ts index 5e776aa9908..63afebab2f6 100644 --- a/src/testRunner/unittests/tsserver/watchEnvironment.ts +++ b/src/testRunner/unittests/tsserver/watchEnvironment.ts @@ -99,7 +99,7 @@ namespace ts.projectSystem { content: "let y = 10;" }; const files = [configFile, file1, file2, libFile]; - const host = createServerHost(files, { useWindowsStylePaths: true }); + const host = createServerHost(files, { windowsStyleRoot: "c:/" }); const projectService = createProjectService(host); projectService.openClientFile(file1.path); const project = projectService.configuredProjects.get(configFile.path)!; @@ -211,4 +211,39 @@ namespace ts.projectSystem { } }); + describe("unittests:: tsserver:: watchEnvironment:: tsserverProjectSystem watching files with network style paths", () => { + function verifyFilePathStyle(path: string) { + const windowsStyleRoot = path.substr(0, getRootLength(path)); + const host = createServerHost( + [libFile, { path, content: "const x = 10" }], + { windowsStyleRoot } + ); + const service = createProjectService(host); + service.openClientFile(path); + checkNumberOfProjects(service, { inferredProjects: 1 }); + const libPath = `${windowsStyleRoot}${libFile.path.substring(1)}`; + checkProjectActualFiles(service.inferredProjects[0], [path, libPath]); + checkWatchedFiles(host, [libPath, `${getDirectoryPath(path)}/tsconfig.json`, `${getDirectoryPath(path)}/jsconfig.json`]); + } + + it("for file of style c:/myprojects/project/x.js", () => { + verifyFilePathStyle("c:/myprojects/project/x.js"); + }); + + it("for file of style //vda1cs4850/myprojects/project/x.js", () => { + verifyFilePathStyle("//vda1cs4850/myprojects/project/x.js"); + }); + + it("for file of style //vda1cs4850/c$/myprojects/project/x.js", () => { + verifyFilePathStyle("//vda1cs4850/c$/myprojects/project/x.js"); + }); + + it("for file of style c:/users/username/myprojects/project/x.js", () => { + verifyFilePathStyle("c:/users/username/myprojects/project/x.js"); + }); + + it("for file of style //vda1cs4850/c$/users/username/myprojects/project/x.js", () => { + verifyFilePathStyle("//vda1cs4850/c$/users/username/myprojects/project/x.js"); + }); + }); }