From ad3af6cbf6d91910ebc2c96de4b696568c56020a Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 17 Dec 2019 13:19:55 -0800 Subject: [PATCH] Fix completions when the ts installation and project are on two different windows drive Fixes #35512 --- src/compiler/moduleSpecifiers.ts | 9 +- src/harness/virtualFileSystemWithWatch.ts | 2 +- .../unittests/tsserver/completions.ts | 128 ++++++++++++++++++ 3 files changed, 137 insertions(+), 2 deletions(-) diff --git a/src/compiler/moduleSpecifiers.ts b/src/compiler/moduleSpecifiers.ts index 1850220a411c1..71a818aad4dd4 100644 --- a/src/compiler/moduleSpecifiers.ts +++ b/src/compiler/moduleSpecifiers.ts @@ -216,7 +216,6 @@ namespace ts.moduleSpecifiers { for ( let directory = getDirectoryPath(toPath(importingFileName, cwd, getCanonicalFileName)); allFileNames.size !== 0; - directory = getDirectoryPath(directory) ) { const directoryStart = ensureTrailingDirectorySeparator(directory); let pathsInDirectory: string[] | undefined; @@ -232,6 +231,14 @@ namespace ts.moduleSpecifiers { } sortedPaths.push(...pathsInDirectory); } + const newDirectory = getDirectoryPath(directory); + if (newDirectory === directory) break; + directory = newDirectory; + } + if (allFileNames.size) { + const remainingPaths = arrayFrom(allFileNames.values()); + if (remainingPaths.length > 1) remainingPaths.sort(comparePathsByNumberOfDirectrorySeparators); + sortedPaths.push(...remainingPaths); } return sortedPaths; } diff --git a/src/harness/virtualFileSystemWithWatch.ts b/src/harness/virtualFileSystemWithWatch.ts index 5b29c8532e6d7..25b8f3efe5027 100644 --- a/src/harness/virtualFileSystemWithWatch.ts +++ b/src/harness/virtualFileSystemWithWatch.ts @@ -645,7 +645,7 @@ interface Array { length: number; [n: number]: T; }` } else { // root folder - Debug.assert(this.fs.size === 0); + Debug.assert(this.fs.size === 0 || !!this.windowsStyleRoot); this.fs.set(path, folder); } } diff --git a/src/testRunner/unittests/tsserver/completions.ts b/src/testRunner/unittests/tsserver/completions.ts index 1e50af89359c5..857f1b6ebc1e0 100644 --- a/src/testRunner/unittests/tsserver/completions.ts +++ b/src/testRunner/unittests/tsserver/completions.ts @@ -118,5 +118,133 @@ namespace ts.projectSystem { } ]); }); + + it("works when files are included from two different drives of windows", () => { + const projectRoot = "e:/myproject"; + const appPackage: File = { + path: `${projectRoot}/package.json`, + content: JSON.stringify({ + name: "test", + version: "0.1.0", + dependencies: { + "react": "^16.12.0", + "react-router-dom": "^5.1.2", + } + }) + }; + const appFile: File = { + path: `${projectRoot}/src/app.js`, + content: `import React from 'react'; +import { + BrowserRouter as Router, +} from "react-router-dom"; +` + }; + const localNodeModules = `${projectRoot}/node_modules`; + const localAtTypes = `${localNodeModules}/@types`; + const localReactPackage: File = { + path: `${localAtTypes}/react/package.json`, + content: JSON.stringify({ + name: "@types/react", + version: "16.9.14", + }) + }; + const localReact: File = { + path: `${localAtTypes}/react/index.d.ts`, + content: `import * as PropTypes from 'prop-types'; +` + }; + const localReactRouterDomPackage: File = { + path: `${localNodeModules}/react-router-dom/package.json`, + content: JSON.stringify({ + name: "react-router-dom", + version: "5.1.2", + }) + }; + const localReactRouterDom: File = { + path: `${localNodeModules}/react-router-dom/index.js`, + content: `export function foo() {}` + }; + const localPropTypesPackage: File = { + path: `${localAtTypes}/prop-types/package.json`, + content: JSON.stringify({ + name: "@types/prop-types", + version: "15.7.3", + }) + }; + const localPropTypes: File = { + path: `${localAtTypes}/prop-types/index.d.ts`, + content: `export type ReactComponentLike = + | string + | ((props: any, context?: any) => any) + | (new (props: any, context?: any) => any); +` + }; + + const globalCacheLocation = `c:/typescript`; + const globalAtTypes = `${globalCacheLocation}/node_modules/@types`; + const globalReactRouterDomPackage: File = { + path: `${globalAtTypes}/react-router-dom/package.json`, + content: JSON.stringify({ + name: "@types/react-router-dom", + version: "5.1.2", + }) + }; + const globalReactRouterDom: File = { + path: `${globalAtTypes}/react-router-dom/index.d.ts`, + content: `import * as React from 'react'; +export interface BrowserRouterProps { + basename?: string; + getUserConfirmation?: ((message: string, callback: (ok: boolean) => void) => void); + forceRefresh?: boolean; + keyLength?: number; +}` + }; + const globalReactPackage: File = { + path: `${globalAtTypes}/react/package.json`, + content: localReactPackage.content + }; + const globalReact: File = { + path: `${globalAtTypes}/react/index.d.ts`, + content: localReact.content + }; + + const filesInProject = [ + appFile, + localReact, + localPropTypes, + globalReactRouterDom, + globalReact, + ]; + const files = [ + ...filesInProject, + appPackage, libFile, + localReactPackage, + localReactRouterDomPackage, localReactRouterDom, + localPropTypesPackage, + globalReactRouterDomPackage, + globalReactPackage, + ]; + + const host = createServerHost(files, { windowsStyleRoot: "c:/" }); + const session = createSession(host, { + typingsInstaller: new TestTypingsInstaller(globalCacheLocation, /*throttleLimit*/ 5, host), + }); + const service = session.getProjectService(); + openFilesForSession([appFile], session); + checkNumberOfProjects(service, { inferredProjects: 1 }); + const windowsStyleLibFilePath = "c:/" + libFile.path.substring(1); + checkProjectActualFiles(service.inferredProjects[0], filesInProject.map(f => f.path).concat(windowsStyleLibFilePath)); + session.executeCommandSeq({ + command: protocol.CommandTypes.CompletionInfo, + arguments: { + file: appFile.path, + line: 5, + offset: 1, + includeExternalModuleExports: true, + includeInsertTextCompletions: true + } + }); + }); }); }