|
2 | 2 | namespace ts.Completions {
|
3 | 3 | export type Log = (message: string) => void;
|
4 | 4 |
|
5 |
| - type SymbolOriginInfo = { type: "this-type" } | { type: "symbol-member" } | SymbolOriginInfoExport; |
| 5 | + const enum SymbolOriginInfoKind { ThisType, SymbolMemberNoExport, SymbolMemberExport, Export } |
| 6 | + type SymbolOriginInfo = { kind: SymbolOriginInfoKind.ThisType } | { kind: SymbolOriginInfoKind.SymbolMemberNoExport } | SymbolOriginInfoExport; |
6 | 7 | interface SymbolOriginInfoExport {
|
7 |
| - type: "export"; |
| 8 | + kind: SymbolOriginInfoKind.SymbolMemberExport | SymbolOriginInfoKind.Export; |
8 | 9 | moduleSymbol: Symbol;
|
9 | 10 | isDefaultExport: boolean;
|
10 | 11 | }
|
| 12 | + function originIsSymbolMember(origin: SymbolOriginInfo): boolean { |
| 13 | + return origin.kind === SymbolOriginInfoKind.SymbolMemberExport || origin.kind === SymbolOriginInfoKind.SymbolMemberNoExport; |
| 14 | + } |
| 15 | + function originIsExport(origin: SymbolOriginInfo): origin is SymbolOriginInfoExport { |
| 16 | + return origin.kind === SymbolOriginInfoKind.SymbolMemberExport || origin.kind === SymbolOriginInfoKind.Export; |
| 17 | + } |
| 18 | + |
11 | 19 | /**
|
12 | 20 | * Map from symbol id -> SymbolOriginInfo.
|
13 | 21 | * Only populated for symbols that come from other modules.
|
@@ -214,12 +222,12 @@ namespace ts.Completions {
|
214 | 222 |
|
215 | 223 | let insertText: string | undefined;
|
216 | 224 | let replacementSpan: TextSpan | undefined;
|
217 |
| - if (origin && origin.type === "this-type") { |
| 225 | + if (origin && origin.kind === SymbolOriginInfoKind.ThisType) { |
218 | 226 | insertText = needsConvertPropertyAccess ? `this[${quote(name, preferences)}]` : `this.${name}`;
|
219 | 227 | }
|
220 | 228 | // We should only have needsConvertPropertyAccess if there's a property access to convert. But see #21790.
|
221 | 229 | // 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.
|
222 |
| - else if ((origin && origin.type === "symbol-member" || needsConvertPropertyAccess) && propertyAccessToConvert) { |
| 230 | + else if ((origin && originIsSymbolMember(origin) || needsConvertPropertyAccess) && propertyAccessToConvert) { |
223 | 231 | insertText = needsConvertPropertyAccess ? `[${quote(name, preferences)}]` : `[${name}]`;
|
224 | 232 | const dot = findChildOfKind(propertyAccessToConvert, SyntaxKind.DotToken, sourceFile)!;
|
225 | 233 | // If the text after the '.' starts with this name, write over it. Else, add new text.
|
@@ -253,7 +261,7 @@ namespace ts.Completions {
|
253 | 261 | kindModifiers: SymbolDisplay.getSymbolModifiers(symbol),
|
254 | 262 | sortText: "0",
|
255 | 263 | source: getSourceFromOrigin(origin),
|
256 |
| - hasAction: trueOrUndefined(!!origin && origin.type === "export"), |
| 264 | + hasAction: trueOrUndefined(!!origin && originIsExport(origin)), |
257 | 265 | isRecommended: trueOrUndefined(isRecommendedCompletionMatch(symbol, recommendedCompletion, typeChecker)),
|
258 | 266 | insertText,
|
259 | 267 | replacementSpan,
|
@@ -283,7 +291,7 @@ namespace ts.Completions {
|
283 | 291 | }
|
284 | 292 |
|
285 | 293 | function getSourceFromOrigin(origin: SymbolOriginInfo | undefined): string | undefined {
|
286 |
| - return origin && origin.type === "export" ? stripQuotes(origin.moduleSymbol.name) : undefined; |
| 294 | + return origin && originIsExport(origin) ? stripQuotes(origin.moduleSymbol.name) : undefined; |
287 | 295 | }
|
288 | 296 |
|
289 | 297 | function getCompletionEntriesFromSymbols(
|
@@ -529,7 +537,7 @@ namespace ts.Completions {
|
529 | 537 | }
|
530 | 538 |
|
531 | 539 | function getSymbolName(symbol: Symbol, origin: SymbolOriginInfo | undefined, target: ScriptTarget): string {
|
532 |
| - return origin && origin.type === "export" && origin.isDefaultExport && symbol.escapedName === InternalSymbolName.Default |
| 540 | + return origin && originIsExport(origin) && origin.isDefaultExport && symbol.escapedName === InternalSymbolName.Default |
533 | 541 | // Name of "export default foo;" is "foo". Name of "export default 0" is the filename converted to camelCase.
|
534 | 542 | ? firstDefined(symbol.declarations, d => isExportAssignment(d) && isIdentifier(d.expression) ? d.expression.text : undefined)
|
535 | 543 | || codefix.moduleSymbolToValidIdentifier(origin.moduleSymbol, target)
|
@@ -648,7 +656,7 @@ namespace ts.Completions {
|
648 | 656 | preferences: UserPreferences,
|
649 | 657 | ): CodeActionsAndSourceDisplay {
|
650 | 658 | const symbolOriginInfo = symbolToOriginInfoMap[getSymbolId(symbol)];
|
651 |
| - if (!symbolOriginInfo || symbolOriginInfo.type !== "export") { |
| 659 | + if (!symbolOriginInfo || !originIsExport(symbolOriginInfo)) { |
652 | 660 | return { codeActions: undefined, sourceDisplay: undefined };
|
653 | 661 | }
|
654 | 662 |
|
@@ -1124,7 +1132,9 @@ namespace ts.Completions {
|
1124 | 1132 | const firstAccessibleSymbol = nameSymbol && getFirstSymbolInChain(nameSymbol, contextToken, typeChecker);
|
1125 | 1133 | if (firstAccessibleSymbol && !symbolToOriginInfoMap[getSymbolId(firstAccessibleSymbol)]) {
|
1126 | 1134 | symbols.push(firstAccessibleSymbol);
|
1127 |
| - symbolToOriginInfoMap[getSymbolId(firstAccessibleSymbol)] = { type: "symbol-member" }; |
| 1135 | + const moduleSymbol = firstAccessibleSymbol.parent; |
| 1136 | + symbolToOriginInfoMap[getSymbolId(firstAccessibleSymbol)] = |
| 1137 | + !moduleSymbol || !isExternalModuleSymbol(moduleSymbol) ? { kind: SymbolOriginInfoKind.SymbolMemberNoExport } : { kind: SymbolOriginInfoKind.SymbolMemberExport, moduleSymbol, isDefaultExport: false }; |
1128 | 1138 | }
|
1129 | 1139 | }
|
1130 | 1140 | else {
|
@@ -1222,7 +1232,7 @@ namespace ts.Completions {
|
1222 | 1232 | const thisType = typeChecker.tryGetThisTypeAt(scopeNode);
|
1223 | 1233 | if (thisType) {
|
1224 | 1234 | for (const symbol of getPropertiesForCompletion(thisType, typeChecker)) {
|
1225 |
| - symbolToOriginInfoMap[getSymbolId(symbol)] = { type: "this-type" }; |
| 1235 | + symbolToOriginInfoMap[getSymbolId(symbol)] = { kind: SymbolOriginInfoKind.ThisType }; |
1226 | 1236 | symbols.push(symbol);
|
1227 | 1237 | }
|
1228 | 1238 | }
|
@@ -1374,7 +1384,7 @@ namespace ts.Completions {
|
1374 | 1384 | symbol = getLocalSymbolForExportDefault(symbol) || symbol;
|
1375 | 1385 | }
|
1376 | 1386 |
|
1377 |
| - const origin: SymbolOriginInfo = { type: "export", moduleSymbol, isDefaultExport }; |
| 1387 | + const origin: SymbolOriginInfoExport = { kind: SymbolOriginInfoKind.Export, moduleSymbol, isDefaultExport }; |
1378 | 1388 | if (detailsEntryId || stringContainsCharactersInOrder(getSymbolName(symbol, origin, target).toLowerCase(), tokenTextLowerCase)) {
|
1379 | 1389 | symbols.push(symbol);
|
1380 | 1390 | symbolToOriginInfoMap[getSymbolId(symbol)] = origin;
|
|
0 commit comments