|
4 | 4 | namespace ts.Completions {
|
5 | 5 | export type Log = (message: string) => void;
|
6 | 6 |
|
7 |
| - type SymbolOriginInfo = { type: "this-type" } | SymbolOriginInfoExport; |
| 7 | + type SymbolOriginInfo = { type: "this-type" } | { type: "symbol-member" } | SymbolOriginInfoExport; |
8 | 8 | interface SymbolOriginInfoExport {
|
9 | 9 | type: "export";
|
10 | 10 | moduleSymbol: Symbol;
|
@@ -208,9 +208,9 @@ namespace ts.Completions {
|
208 | 208 | }
|
209 | 209 | // We should only have needsConvertPropertyAccess if there's a property access to convert. But see #21790.
|
210 | 210 | // 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.
|
211 |
| - else if (needsConvertPropertyAccess && propertyAccessToConvert) { |
212 |
| - insertText = `[${quote(name, preferences)}]`; |
213 |
| - const dot = findChildOfKind(propertyAccessToConvert, SyntaxKind.DotToken, sourceFile)!; |
| 211 | + else if ((origin && origin.type === "symbol-member" || needsConvertPropertyAccess) && propertyAccessToConvert) { |
| 212 | + insertText = needsConvertPropertyAccess ? `[${quote(name, preferences)}]` : `[${name}]`; |
| 213 | + const dot = findChildOfKind(propertyAccessToConvert!, SyntaxKind.DotToken, sourceFile)!; |
214 | 214 | // If the text after the '.' starts with this name, write over it. Else, add new text.
|
215 | 215 | const end = startsWith(name, propertyAccessToConvert.name.text) ? propertyAccessToConvert.name.end : dot.end;
|
216 | 216 | replacementSpan = createTextSpanFromBounds(dot.getStart(sourceFile), end);
|
@@ -1064,12 +1064,33 @@ namespace ts.Completions {
|
1064 | 1064 | else {
|
1065 | 1065 | for (const symbol of type.getApparentProperties()) {
|
1066 | 1066 | if (typeChecker.isValidPropertyAccessForCompletions(<PropertyAccessExpression>(node.parent), type, symbol)) {
|
1067 |
| - symbols.push(symbol); |
| 1067 | + addPropertySymbol(symbol); |
1068 | 1068 | }
|
1069 | 1069 | }
|
1070 | 1070 | }
|
1071 | 1071 | }
|
1072 | 1072 |
|
| 1073 | + function addPropertySymbol(symbol: Symbol) { |
| 1074 | + // If this is e.g. [Symbol.iterator], add a completion for `Symbol`. |
| 1075 | + const symbolSymbol = firstDefined(symbol.declarations, decl => { |
| 1076 | + const name = getNameOfDeclaration(decl); |
| 1077 | + const leftName = name.kind === SyntaxKind.ComputedPropertyName ? getLeftMostName(name.expression) : undefined; |
| 1078 | + return leftName && typeChecker.getSymbolAtLocation(leftName); |
| 1079 | + }); |
| 1080 | + if (symbolSymbol) { |
| 1081 | + symbols.push(symbolSymbol); |
| 1082 | + symbolToOriginInfoMap[getSymbolId(symbolSymbol)] = { type: "symbol-member" }; |
| 1083 | + } |
| 1084 | + else { |
| 1085 | + symbols.push(symbol); |
| 1086 | + } |
| 1087 | + } |
| 1088 | + |
| 1089 | + /** Given 'a.b.c', returns 'a'. */ |
| 1090 | + function getLeftMostName(e: Expression): Identifier | undefined { |
| 1091 | + return isIdentifier(e) ? e : isPropertyAccessExpression(e) ? getLeftMostName(e.expression) : undefined; |
| 1092 | + } |
| 1093 | + |
1073 | 1094 | function tryGetGlobalSymbols(): boolean {
|
1074 | 1095 | const result: GlobalsSearch = tryGetObjectLikeCompletionSymbols()
|
1075 | 1096 | || tryGetImportOrExportClauseCompletionSymbols()
|
|
0 commit comments