Merge pull request #21131 from amcasey/GH15533

Unmangle scoped package names in import completions
This commit is contained in:
Andrew Casey 2018-01-11 11:03:34 -08:00 committed by GitHub
commit e3da8fb526
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 69 additions and 17 deletions

View File

@ -1097,13 +1097,18 @@ namespace ts {
export function getPackageNameFromAtTypesDirectory(mangledName: string): string {
const withoutAtTypePrefix = removePrefix(mangledName, "@types/");
if (withoutAtTypePrefix !== mangledName) {
return stringContains(withoutAtTypePrefix, mangledScopedPackageSeparator) ?
"@" + withoutAtTypePrefix.replace(mangledScopedPackageSeparator, ts.directorySeparator) :
withoutAtTypePrefix;
return getUnmangledNameForScopedPackage(withoutAtTypePrefix);
}
return mangledName;
}
/* @internal */
export function getUnmangledNameForScopedPackage(typesPackageName: string): string {
return stringContains(typesPackageName, mangledScopedPackageSeparator) ?
"@" + typesPackageName.replace(mangledScopedPackageSeparator, ts.directorySeparator) :
typesPackageName;
}
function tryFindNonRelativeModuleNameInCache(cache: PerModuleNameCache | undefined, moduleName: string, containingDirectory: string, traceEnabled: boolean, host: ModuleResolutionHost): SearchResult<Resolved> {
const result = cache && cache.get(containingDirectory);
if (result) {

View File

@ -314,9 +314,11 @@ namespace ts.Completions.PathCompletions {
function getCompletionEntriesFromTypings(host: LanguageServiceHost, options: CompilerOptions, scriptPath: string, span: TextSpan, result: CompletionEntry[] = []): CompletionEntry[] {
// Check for typings specified in compiler options
const seen = createMap<true>();
if (options.types) {
for (const moduleName of options.types) {
result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName, span));
for (const typesName of options.types) {
const moduleName = getUnmangledNameForScopedPackage(typesName);
pushResult(moduleName);
}
}
else if (host.getDirectories) {
@ -328,32 +330,40 @@ namespace ts.Completions.PathCompletions {
if (typeRoots) {
for (const root of typeRoots) {
getCompletionEntriesFromDirectories(host, root, span, result);
getCompletionEntriesFromDirectories(root);
}
}
}
if (host.getDirectories) {
// Also get all @types typings installed in visible node_modules directories
for (const packageJson of findPackageJsons(scriptPath, host)) {
const typesDir = combinePaths(getDirectoryPath(packageJson), "node_modules/@types");
getCompletionEntriesFromDirectories(host, typesDir, span, result);
getCompletionEntriesFromDirectories(typesDir);
}
}
return result;
}
function getCompletionEntriesFromDirectories(host: LanguageServiceHost, directory: string, span: TextSpan, result: Push<CompletionEntry>) {
if (host.getDirectories && tryDirectoryExists(host, directory)) {
const directories = tryGetDirectories(host, directory);
if (directories) {
for (let typeDirectory of directories) {
typeDirectory = normalizePath(typeDirectory);
result.push(createCompletionEntryForModule(getBaseFileName(typeDirectory), ScriptElementKind.externalModuleName, span));
function getCompletionEntriesFromDirectories(directory: string) {
Debug.assert(!!host.getDirectories);
if (tryDirectoryExists(host, directory)) {
const directories = tryGetDirectories(host, directory);
if (directories) {
for (let typeDirectory of directories) {
typeDirectory = normalizePath(typeDirectory);
const directoryName = getBaseFileName(typeDirectory);
const moduleName = getUnmangledNameForScopedPackage(directoryName);
pushResult(moduleName);
}
}
}
}
function pushResult(moduleName: string) {
if (!seen.has(moduleName)) {
result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName, span));
seen.set(moduleName, true);
}
}
}
function findPackageJsons(directory: string, host: LanguageServiceHost): string[] {

View File

@ -0,0 +1,20 @@
/// <reference path='fourslash.ts' />
// @Filename: app.ts
////import * as A from "[|/*1*/|]";
// @Filename: /node_modules/@types/a__b/index.d.ts
////declare module "@e/f" { function fun(): string; }
// @Filename: /node_modules/@types/c__d/index.d.ts
////export declare let x: number;
// NOTE: The node_modules folder is in "/", rather than ".", because it requires
// less scaffolding to mock. In particular, "/" is where we look for type roots.
const [replacementSpan] = test.ranges();
verify.completionsAt("1", [
{ name: "@a/b", replacementSpan },
{ name: "@c/d", replacementSpan },
{ name: "@e/f", replacementSpan },
]);

View File

@ -0,0 +1,17 @@
/// <reference path='fourslash.ts' />
// @typeRoots: T1,T2
// @Filename: app.ts
////import * as A from "[|/*1*/|]";
// @Filename: T1/a__b/index.d.ts
////export declare let x: number;
// @Filename: T2/a__b/index.d.ts
////export declare let x: number;
// Confirm that entries are de-dup'd.
verify.completionsAt("1", [
{ name: "@a/b", replacementSpan: test.ranges()[0] },
]);