Simplify tryGetImportOrExportClauseCompletionSymbols (#22961)

* Simplify tryGetImportOrExportClauseCompletionSymbols

* Handle undefined key in arrayToSet
This commit is contained in:
Andy
2018-04-12 11:55:21 -07:00
committed by GitHub
parent d2dc2e6d48
commit afcced6839
3 changed files with 22 additions and 70 deletions

View File

@@ -1314,12 +1314,13 @@ namespace ts {
* the same key with the given 'makeKey' function, then the element with the higher
* index in the array will be the one associated with the produced key.
*/
export function arrayToMap<T>(array: ReadonlyArray<T>, makeKey: (value: T) => string): Map<T>;
export function arrayToMap<T, U>(array: ReadonlyArray<T>, makeKey: (value: T) => string, makeValue: (value: T) => U): Map<U>;
export function arrayToMap<T, U>(array: ReadonlyArray<T>, makeKey: (value: T) => string, makeValue: (value: T) => T | U = identity): Map<T | U> {
export function arrayToMap<T>(array: ReadonlyArray<T>, makeKey: (value: T) => string | undefined): Map<T>;
export function arrayToMap<T, U>(array: ReadonlyArray<T>, makeKey: (value: T) => string | undefined, makeValue: (value: T) => U): Map<U>;
export function arrayToMap<T, U>(array: ReadonlyArray<T>, makeKey: (value: T) => string | undefined, makeValue: (value: T) => T | U = identity): Map<T | U> {
const result = createMap<T | U>();
for (const value of array) {
result.set(makeKey(value), makeValue(value));
const key = makeKey(value);
if (key !== undefined) result.set(key, makeValue(value));
}
return result;
}
@@ -1340,8 +1341,9 @@ namespace ts {
* @param array the array of input elements.
*/
export function arrayToSet(array: ReadonlyArray<string>): Map<true>;
export function arrayToSet<T>(array: ReadonlyArray<T>, makeKey: (value: T) => string): Map<true>;
export function arrayToSet(array: ReadonlyArray<any>, makeKey?: (value: any) => string): Map<true> {
export function arrayToSet<T>(array: ReadonlyArray<T>, makeKey: (value: T) => string | undefined): Map<true>;
export function arrayToSet<T>(array: ReadonlyArray<T>, makeKey: (value: T) => __String | undefined): UnderscoreEscapedMap<true>;
export function arrayToSet(array: ReadonlyArray<any>, makeKey?: (value: any) => string | __String | undefined): Map<true> | UnderscoreEscapedMap<true> {
return arrayToMap<any, true>(array, makeKey || (s => s), () => true);
}

View File

@@ -6325,4 +6325,9 @@ namespace ts {
export function isStringLiteralLike(node: Node): node is StringLiteralLike {
return node.kind === SyntaxKind.StringLiteral || node.kind === SyntaxKind.NoSubstitutionTemplateLiteral;
}
/** @internal */
export function isNamedImportsOrExports(node: Node): node is NamedImportsOrExports {
return node.kind === SyntaxKind.NamedImports || node.kind === SyntaxKind.NamedExports;
}
}

View File

@@ -1555,32 +1555,22 @@ namespace ts.Completions {
* @returns true if 'symbols' was successfully populated; false otherwise.
*/
function tryGetImportOrExportClauseCompletionSymbols(): GlobalsSearch {
const namedImportsOrExports = tryGetNamedImportsOrExportsForCompletion(contextToken);
if (!namedImportsOrExports) return undefined;
// `import { |` or `import { a as 0, | }`
const namedImportsOrExports = contextToken && (contextToken.kind === SyntaxKind.OpenBraceToken || contextToken.kind === SyntaxKind.CommaToken)
? tryCast(contextToken.parent, isNamedImportsOrExports) : undefined;
if (!namedImportsOrExports) return GlobalsSearch.Continue;
// cursor is in an import clause
// try to show exported member for imported module
const declarationKind = namedImportsOrExports.kind === SyntaxKind.NamedImports ?
SyntaxKind.ImportDeclaration :
SyntaxKind.ExportDeclaration;
const importOrExportDeclaration = <ImportDeclaration | ExportDeclaration>getAncestor(namedImportsOrExports, declarationKind);
const moduleSpecifier = importOrExportDeclaration.moduleSpecifier;
if (!moduleSpecifier) {
return GlobalsSearch.Fail;
}
const { moduleSpecifier } = namedImportsOrExports.kind === SyntaxKind.NamedImports ? namedImportsOrExports.parent.parent : namedImportsOrExports.parent;
const moduleSpecifierSymbol = typeChecker.getSymbolAtLocation(moduleSpecifier);
if (!moduleSpecifierSymbol) return GlobalsSearch.Fail;
completionKind = CompletionKind.MemberLike;
isNewIdentifierLocation = false;
const moduleSpecifierSymbol = typeChecker.getSymbolAtLocation(moduleSpecifier);
if (!moduleSpecifierSymbol) {
symbols = emptyArray;
return GlobalsSearch.Fail;
}
const exports = typeChecker.getExportsAndPropertiesOfModule(moduleSpecifierSymbol);
symbols = filterNamedImportOrExportCompletionItems(exports, namedImportsOrExports.elements);
const existing = arrayToSet<ImportOrExportSpecifier>(namedImportsOrExports.elements, n => isCurrentlyEditingNode(n) ? undefined : (n.propertyName || n.name).escapedText);
symbols = exports.filter(e => e.escapedName !== InternalSymbolName.Default && !existing.get(e.escapedName));
return GlobalsSearch.Success;
}
@@ -1648,26 +1638,6 @@ namespace ts.Completions {
return undefined;
}
/**
* Returns the containing list of named imports or exports of a context token,
* on the condition that one exists and that the context implies completion should be given.
*/
function tryGetNamedImportsOrExportsForCompletion(contextToken: Node): NamedImportsOrExports {
if (contextToken) {
switch (contextToken.kind) {
case SyntaxKind.OpenBraceToken: // import { |
case SyntaxKind.CommaToken: // import { a as 0, |
switch (contextToken.parent.kind) {
case SyntaxKind.NamedImports:
case SyntaxKind.NamedExports:
return <NamedImportsOrExports>contextToken.parent;
}
}
}
return undefined;
}
function isConstructorParameterCompletion(node: Node): boolean {
return !!node.parent && isParameter(node.parent) && isConstructorDeclaration(node.parent.parent)
&& (isParameterPropertyModifier(node.kind) || isDeclarationName(node));
@@ -1911,31 +1881,6 @@ namespace ts.Completions {
return false;
}
/**
* Filters out completion suggestions for named imports or exports.
*
* @param exportsOfModule The list of symbols which a module exposes.
* @param namedImportsOrExports The list of existing import/export specifiers in the import/export clause.
*
* @returns Symbols to be suggested at an import/export clause, barring those whose named imports/exports
* do not occur at the current position and have not otherwise been typed.
*/
function filterNamedImportOrExportCompletionItems(exportsOfModule: Symbol[], namedImportsOrExports: ReadonlyArray<ImportOrExportSpecifier>): Symbol[] {
const existingImportsOrExports = createUnderscoreEscapedMap<boolean>();
for (const element of namedImportsOrExports) {
// If this is the current item we are editing right now, do not filter it out
if (isCurrentlyEditingNode(element)) {
continue;
}
const name = element.propertyName || element.name;
existingImportsOrExports.set(name.escapedText, true);
}
return exportsOfModule.filter(e => e.escapedName !== InternalSymbolName.Default && !existingImportsOrExports.get(e.escapedName));
}
/**
* Filters out completion suggestions for named imports or exports.
*