Support completions for members with symbol names (#23091)

This commit is contained in:
Andy
2018-04-02 18:08:00 -07:00
committed by GitHub
parent 751eab9eed
commit 1e04bd4168
2 changed files with 41 additions and 5 deletions

View File

@@ -4,7 +4,7 @@
namespace ts.Completions {
export type Log = (message: string) => void;
type SymbolOriginInfo = { type: "this-type" } | SymbolOriginInfoExport;
type SymbolOriginInfo = { type: "this-type" } | { type: "symbol-member" } | SymbolOriginInfoExport;
interface SymbolOriginInfoExport {
type: "export";
moduleSymbol: Symbol;
@@ -208,9 +208,9 @@ namespace ts.Completions {
}
// We should only have needsConvertPropertyAccess if there's a property access to convert. But see #21790.
// Somehow there was a global with a non-identifier name. Hopefully someone will complain about getting a "foo bar" global completion and provide a repro.
else if (needsConvertPropertyAccess && propertyAccessToConvert) {
insertText = `[${quote(name, preferences)}]`;
const dot = findChildOfKind(propertyAccessToConvert, SyntaxKind.DotToken, sourceFile)!;
else if ((origin && origin.type === "symbol-member" || needsConvertPropertyAccess) && propertyAccessToConvert) {
insertText = needsConvertPropertyAccess ? `[${quote(name, preferences)}]` : `[${name}]`;
const dot = findChildOfKind(propertyAccessToConvert!, SyntaxKind.DotToken, sourceFile)!;
// If the text after the '.' starts with this name, write over it. Else, add new text.
const end = startsWith(name, propertyAccessToConvert.name.text) ? propertyAccessToConvert.name.end : dot.end;
replacementSpan = createTextSpanFromBounds(dot.getStart(sourceFile), end);
@@ -1064,12 +1064,33 @@ namespace ts.Completions {
else {
for (const symbol of type.getApparentProperties()) {
if (typeChecker.isValidPropertyAccessForCompletions(<PropertyAccessExpression>(node.parent), type, symbol)) {
symbols.push(symbol);
addPropertySymbol(symbol);
}
}
}
}
function addPropertySymbol(symbol: Symbol) {
// If this is e.g. [Symbol.iterator], add a completion for `Symbol`.
const symbolSymbol = firstDefined(symbol.declarations, decl => {
const name = getNameOfDeclaration(decl);
const leftName = name.kind === SyntaxKind.ComputedPropertyName ? getLeftMostName(name.expression) : undefined;
return leftName && typeChecker.getSymbolAtLocation(leftName);
});
if (symbolSymbol) {
symbols.push(symbolSymbol);
symbolToOriginInfoMap[getSymbolId(symbolSymbol)] = { type: "symbol-member" };
}
else {
symbols.push(symbol);
}
}
/** Given 'a.b.c', returns 'a'. */
function getLeftMostName(e: Expression): Identifier | undefined {
return isIdentifier(e) ? e : isPropertyAccessExpression(e) ? getLeftMostName(e.expression) : undefined;
}
function tryGetGlobalSymbols(): boolean {
const result: GlobalsSearch = tryGetObjectLikeCompletionSymbols()
|| tryGetImportOrExportClauseCompletionSymbols()