• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/* @internal */
2namespace ts.Completions {
3    export enum SortText {
4        LocalDeclarationPriority = "0",
5        LocationPriority = "1",
6        OptionalMember = "2",
7        MemberDeclaredBySpreadAssignment = "3",
8        SuggestedClassMembers = "4",
9        GlobalsOrKeywords = "5",
10        AutoImportSuggestions = "6",
11        JavascriptIdentifiers = "7"
12    }
13    export type Log = (message: string) => void;
14
15    /**
16     * Special values for `CompletionInfo['source']` used to disambiguate
17     * completion items with the same `name`. (Each completion item must
18     * have a unique name/source combination, because those two fields
19     * comprise `CompletionEntryIdentifier` in `getCompletionEntryDetails`.
20     *
21     * When the completion item is an auto-import suggestion, the source
22     * is the module specifier of the suggestion. To avoid collisions,
23     * the values here should not be a module specifier we would ever
24     * generate for an auto-import.
25     */
26    export enum CompletionSource {
27        /** Completions that require `this.` insertion text */
28        ThisProperty = "ThisProperty/"
29    }
30
31    const enum SymbolOriginInfoKind {
32        ThisType = 1 << 0,
33        SymbolMember = 1 << 1,
34        Export = 1 << 2,
35        Promise = 1 << 3,
36        Nullable = 1 << 4,
37
38        SymbolMemberNoExport = SymbolMember,
39        SymbolMemberExport = SymbolMember | Export,
40    }
41
42    interface SymbolOriginInfo {
43        kind: SymbolOriginInfoKind;
44    }
45
46    interface SymbolOriginInfoExport extends SymbolOriginInfo {
47        kind: SymbolOriginInfoKind;
48        moduleSymbol: Symbol;
49        isDefaultExport: boolean;
50        isFromPackageJson?: boolean;
51    }
52
53    function originIsThisType(origin: SymbolOriginInfo): boolean {
54        return !!(origin.kind & SymbolOriginInfoKind.ThisType);
55    }
56
57    function originIsSymbolMember(origin: SymbolOriginInfo): boolean {
58        return !!(origin.kind & SymbolOriginInfoKind.SymbolMember);
59    }
60
61    function originIsExport(origin: SymbolOriginInfo | undefined): origin is SymbolOriginInfoExport {
62        return !!(origin && origin.kind & SymbolOriginInfoKind.Export);
63    }
64
65    function originIsPackageJsonImport(origin: SymbolOriginInfo | undefined): origin is SymbolOriginInfoExport {
66        return originIsExport(origin) && !!origin.isFromPackageJson;
67    }
68
69    function originIsPromise(origin: SymbolOriginInfo): boolean {
70        return !!(origin.kind & SymbolOriginInfoKind.Promise);
71    }
72
73    function originIsNullableMember(origin: SymbolOriginInfo): boolean {
74        return !!(origin.kind & SymbolOriginInfoKind.Nullable);
75    }
76
77    interface UniqueNameSet {
78        add(name: string): void;
79        has(name: string): boolean;
80    }
81
82    /**
83     * Map from symbol id -> SymbolOriginInfo.
84     * Only populated for symbols that come from other modules.
85     */
86    type SymbolOriginInfoMap = (SymbolOriginInfo | SymbolOriginInfoExport | undefined)[];
87
88    type SymbolSortTextMap = (SortText | undefined)[];
89
90    const enum KeywordCompletionFilters {
91        None,                           // No keywords
92        All,                            // Every possible keyword (TODO: This is never appropriate)
93        ClassElementKeywords,           // Keywords inside class body
94        InterfaceElementKeywords,       // Keywords inside interface body
95        ConstructorParameterKeywords,   // Keywords at constructor parameter
96        FunctionLikeBodyKeywords,       // Keywords at function like body
97        TypeAssertionKeywords,
98        TypeKeywords,
99        Last = TypeKeywords
100    }
101
102    const enum GlobalsSearch { Continue, Success, Fail }
103
104    export interface AutoImportSuggestion {
105        symbol: Symbol;
106        symbolName: string;
107        skipFilter: boolean;
108        origin: SymbolOriginInfoExport;
109    }
110    export interface ImportSuggestionsForFileCache {
111        clear(): void;
112        get(fileName: string, checker: TypeChecker, projectVersion?: string): readonly AutoImportSuggestion[] | undefined;
113        set(fileName: string, suggestions: readonly AutoImportSuggestion[], projectVersion?: string): void;
114        isEmpty(): boolean;
115    }
116    export function createImportSuggestionsForFileCache(): ImportSuggestionsForFileCache {
117        let cache: readonly AutoImportSuggestion[] | undefined;
118        let projectVersion: string | undefined;
119        let fileName: string | undefined;
120        return {
121            isEmpty() {
122                return !cache;
123            },
124            clear: () => {
125                cache = undefined;
126                fileName = undefined;
127                projectVersion = undefined;
128            },
129            set: (file, suggestions, version) => {
130                cache = suggestions;
131                fileName = file;
132                if (version) {
133                    projectVersion = version;
134                }
135            },
136            get: (file, checker, version) => {
137                if (file !== fileName) {
138                    return undefined;
139                }
140                if (version) {
141                    return projectVersion === version ? cache : undefined;
142                }
143                forEach(cache, suggestion => {
144                    // If the symbol/moduleSymbol was a merged symbol, it will have a new identity
145                    // in the checker, even though the symbols to merge are the same (guaranteed by
146                    // cache invalidation in synchronizeHostData).
147                    if (suggestion.symbol.declarations?.length) {
148                        suggestion.symbol = checker.getMergedSymbol(suggestion.origin.isDefaultExport
149                            ? suggestion.symbol.declarations[0].localSymbol ?? suggestion.symbol.declarations[0].symbol
150                            : suggestion.symbol.declarations[0].symbol);
151                    }
152                    if (suggestion.origin.moduleSymbol.declarations?.length) {
153                        suggestion.origin.moduleSymbol = checker.getMergedSymbol(suggestion.origin.moduleSymbol.declarations[0].symbol);
154                    }
155                });
156                return cache;
157            },
158        };
159    }
160
161    export function getCompletionsAtPosition(
162        host: LanguageServiceHost,
163        program: Program,
164        log: Log,
165        sourceFile: SourceFile,
166        position: number,
167        preferences: UserPreferences,
168        triggerCharacter: CompletionsTriggerCharacter | undefined
169    ): CompletionInfo | undefined {
170        const typeChecker = program.getTypeChecker();
171        const compilerOptions = program.getCompilerOptions();
172
173        const contextToken = findPrecedingToken(position, sourceFile);
174        if (triggerCharacter && !isInString(sourceFile, position, contextToken) && !isValidTrigger(sourceFile, triggerCharacter, contextToken, position)) {
175            return undefined;
176        }
177
178        const stringCompletions = StringCompletions.getStringLiteralCompletions(sourceFile, position, contextToken, typeChecker, compilerOptions, host, log, preferences);
179        if (stringCompletions) {
180            return stringCompletions;
181        }
182
183        if (contextToken && isBreakOrContinueStatement(contextToken.parent)
184            && (contextToken.kind === SyntaxKind.BreakKeyword || contextToken.kind === SyntaxKind.ContinueKeyword || contextToken.kind === SyntaxKind.Identifier)) {
185            return getLabelCompletionAtPosition(contextToken.parent);
186        }
187
188        const completionData = getCompletionData(program, log, sourceFile, isUncheckedFile(sourceFile, compilerOptions), position, preferences, /*detailsEntryId*/ undefined, host);
189        if (!completionData) {
190            return undefined;
191        }
192
193        switch (completionData.kind) {
194            case CompletionDataKind.Data:
195                return completionInfoFromData(sourceFile, typeChecker, compilerOptions, log, completionData, preferences);
196            case CompletionDataKind.JsDocTagName:
197                // If the current position is a jsDoc tag name, only tag names should be provided for completion
198                return jsdocCompletionInfo(JsDoc.getJSDocTagNameCompletions());
199            case CompletionDataKind.JsDocTag:
200                // If the current position is a jsDoc tag, only tags should be provided for completion
201                return jsdocCompletionInfo(JsDoc.getJSDocTagCompletions());
202            case CompletionDataKind.JsDocParameterName:
203                return jsdocCompletionInfo(JsDoc.getJSDocParameterNameCompletions(completionData.tag));
204            default:
205                return Debug.assertNever(completionData);
206        }
207    }
208
209    function jsdocCompletionInfo(entries: CompletionEntry[]): CompletionInfo {
210        return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, entries };
211    }
212
213    function getOptionalReplacementSpan(location: Node | undefined) {
214        // StringLiteralLike locations are handled separately in stringCompletions.ts
215        return location?.kind === SyntaxKind.Identifier ? createTextSpanFromNode(location) : undefined;
216    }
217
218    function completionInfoFromData(sourceFile: SourceFile, typeChecker: TypeChecker, compilerOptions: CompilerOptions, log: Log, completionData: CompletionData, preferences: UserPreferences): CompletionInfo | undefined {
219        const {
220            symbols,
221            completionKind,
222            isInSnippetScope,
223            isNewIdentifierLocation,
224            location,
225            propertyAccessToConvert,
226            keywordFilters,
227            literals,
228            symbolToOriginInfoMap,
229            recommendedCompletion,
230            isJsxInitializer,
231            insideJsDocTagTypeExpression,
232            symbolToSortTextMap,
233        } = completionData;
234
235        if (location && location.parent && isJsxClosingElement(location.parent)) {
236            // In the TypeScript JSX element, if such element is not defined. When users query for completion at closing tag,
237            // instead of simply giving unknown value, the completion will return the tag-name of an associated opening-element.
238            // For example:
239            //     var x = <div> </ /*1*/
240            // The completion list at "1" will contain "div>" with type any
241            // And at `<div> </ /*1*/ >` (with a closing `>`), the completion list will contain "div".
242            const tagName = location.parent.parent.openingElement.tagName;
243            const hasClosingAngleBracket = !!findChildOfKind(location.parent, SyntaxKind.GreaterThanToken, sourceFile);
244            const entry: CompletionEntry = {
245                name: tagName.getFullText(sourceFile) + (hasClosingAngleBracket ? "" : ">"),
246                kind: ScriptElementKind.classElement,
247                kindModifiers: undefined,
248                sortText: SortText.LocationPriority,
249            };
250            return { isGlobalCompletion: false, isMemberCompletion: true, isNewIdentifierLocation: false, optionalReplacementSpan: getOptionalReplacementSpan(location), entries: [entry] };
251        }
252
253        const entries: CompletionEntry[] = [];
254
255        if (isUncheckedFile(sourceFile, compilerOptions)) {
256            const uniqueNames = getCompletionEntriesFromSymbols(
257                symbols,
258                entries,
259                /* contextToken */ undefined,
260                location,
261                sourceFile,
262                typeChecker,
263                compilerOptions.target!,
264                log,
265                completionKind,
266                preferences,
267                propertyAccessToConvert,
268                completionData.isJsxIdentifierExpected,
269                isJsxInitializer,
270                recommendedCompletion,
271                symbolToOriginInfoMap,
272                symbolToSortTextMap
273            );
274            getJSCompletionEntries(sourceFile, location!.pos, uniqueNames, compilerOptions.target!, entries); // TODO: GH#18217
275        }
276        else {
277            if (!isNewIdentifierLocation && (!symbols || symbols.length === 0) && keywordFilters === KeywordCompletionFilters.None) {
278                return undefined;
279            }
280
281            getCompletionEntriesFromSymbols(
282                symbols,
283                entries,
284                /* contextToken */ undefined,
285                location,
286                sourceFile,
287                typeChecker,
288                compilerOptions.target!,
289                log,
290                completionKind,
291                preferences,
292                propertyAccessToConvert,
293                completionData.isJsxIdentifierExpected,
294                isJsxInitializer,
295                recommendedCompletion,
296                symbolToOriginInfoMap,
297                symbolToSortTextMap
298            );
299        }
300
301        if (keywordFilters !== KeywordCompletionFilters.None) {
302            const entryNames = new Set(entries.map(e => e.name));
303            for (const keywordEntry of getKeywordCompletions(keywordFilters, !insideJsDocTagTypeExpression && isSourceFileJS(sourceFile))) {
304                if (!entryNames.has(keywordEntry.name)) {
305                    entries.push(keywordEntry);
306                }
307            }
308        }
309
310        for (const literal of literals) {
311            entries.push(createCompletionEntryForLiteral(sourceFile, preferences, literal));
312        }
313
314        return {
315            isGlobalCompletion: isInSnippetScope,
316            isMemberCompletion: isMemberCompletionKind(completionKind),
317            isNewIdentifierLocation,
318            optionalReplacementSpan: getOptionalReplacementSpan(location),
319            entries
320        };
321    }
322
323    function isUncheckedFile(sourceFile: SourceFile, compilerOptions: CompilerOptions): boolean {
324        return isSourceFileJS(sourceFile) && !isCheckJsEnabledForFile(sourceFile, compilerOptions);
325    }
326
327    function isMemberCompletionKind(kind: CompletionKind): boolean {
328        switch (kind) {
329            case CompletionKind.ObjectPropertyDeclaration:
330            case CompletionKind.MemberLike:
331            case CompletionKind.PropertyAccess:
332                return true;
333            default:
334                return false;
335        }
336    }
337
338    function getJSCompletionEntries(
339        sourceFile: SourceFile,
340        position: number,
341        uniqueNames: UniqueNameSet,
342        target: ScriptTarget,
343        entries: Push<CompletionEntry>): void {
344        getNameTable(sourceFile).forEach((pos, name) => {
345            // Skip identifiers produced only from the current location
346            if (pos === position) {
347                return;
348            }
349            const realName = unescapeLeadingUnderscores(name);
350            if (!uniqueNames.has(realName) && isIdentifierText(realName, target)) {
351                uniqueNames.add(realName);
352                entries.push({
353                    name: realName,
354                    kind: ScriptElementKind.warning,
355                    kindModifiers: "",
356                    sortText: SortText.JavascriptIdentifiers,
357                    isFromUncheckedFile: true
358                });
359            }
360        });
361    }
362
363    function completionNameForLiteral(sourceFile: SourceFile, preferences: UserPreferences, literal: string | number | PseudoBigInt): string {
364        return typeof literal === "object" ? pseudoBigIntToString(literal) + "n" :
365            isString(literal) ? quote(sourceFile, preferences, literal) : JSON.stringify(literal);
366    }
367
368    function createCompletionEntryForLiteral(sourceFile: SourceFile, preferences: UserPreferences, literal: string | number | PseudoBigInt): CompletionEntry {
369        return { name: completionNameForLiteral(sourceFile, preferences, literal), kind: ScriptElementKind.string, kindModifiers: ScriptElementKindModifier.none, sortText: SortText.LocationPriority };
370    }
371
372    function createCompletionEntry(
373        symbol: Symbol,
374        sortText: SortText,
375        contextToken: Node | undefined,
376        location: Node | undefined,
377        sourceFile: SourceFile,
378        typeChecker: TypeChecker,
379        name: string,
380        needsConvertPropertyAccess: boolean,
381        origin: SymbolOriginInfo | undefined,
382        recommendedCompletion: Symbol | undefined,
383        propertyAccessToConvert: PropertyAccessExpression | undefined,
384        isJsxInitializer: IsJsxInitializer | undefined,
385        preferences: UserPreferences,
386    ): CompletionEntry | undefined {
387        let insertText: string | undefined;
388        let replacementSpan = getReplacementSpanForContextToken(contextToken);
389
390        const insertQuestionDot = origin && originIsNullableMember(origin);
391        const useBraces = origin && originIsSymbolMember(origin) || needsConvertPropertyAccess;
392        if (origin && originIsThisType(origin)) {
393            insertText = needsConvertPropertyAccess
394                ? `this${insertQuestionDot ? "?." : ""}[${quotePropertyName(sourceFile, preferences, name)}]`
395                : `this${insertQuestionDot ? "?." : "."}${name}`;
396        }
397        // We should only have needsConvertPropertyAccess if there's a property access to convert. But see #21790.
398        // 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.
399        else if ((useBraces || insertQuestionDot) && propertyAccessToConvert) {
400            insertText = useBraces ? needsConvertPropertyAccess ? `[${quotePropertyName(sourceFile, preferences, name)}]` : `[${name}]` : name;
401            if (insertQuestionDot || propertyAccessToConvert.questionDotToken) {
402                insertText = `?.${insertText}`;
403            }
404
405            const dot = findChildOfKind(propertyAccessToConvert, SyntaxKind.DotToken, sourceFile) ||
406                findChildOfKind(propertyAccessToConvert, SyntaxKind.QuestionDotToken, sourceFile);
407            if (!dot) {
408                return undefined;
409            }
410            // If the text after the '.' starts with this name, write over it. Else, add new text.
411            const end = startsWith(name, propertyAccessToConvert.name.text) ? propertyAccessToConvert.name.end : dot.end;
412            replacementSpan = createTextSpanFromBounds(dot.getStart(sourceFile), end);
413        }
414
415        if (isJsxInitializer) {
416            if (insertText === undefined) insertText = name;
417            insertText = `{${insertText}}`;
418            if (typeof isJsxInitializer !== "boolean") {
419                replacementSpan = createTextSpanFromNode(isJsxInitializer, sourceFile);
420            }
421        }
422        if (origin && originIsPromise(origin) && propertyAccessToConvert) {
423            if (insertText === undefined) insertText = name;
424            const precedingToken = findPrecedingToken(propertyAccessToConvert.pos, sourceFile);
425            let awaitText = "";
426            if (precedingToken && positionIsASICandidate(precedingToken.end, precedingToken.parent, sourceFile)) {
427                awaitText = ";";
428            }
429
430            awaitText += `(await ${propertyAccessToConvert.expression.getText()})`;
431            insertText = needsConvertPropertyAccess ? `${awaitText}${insertText}` : `${awaitText}${insertQuestionDot ? "?." : "."}${insertText}`;
432            replacementSpan = createTextSpanFromBounds(propertyAccessToConvert.getStart(sourceFile), propertyAccessToConvert.end);
433        }
434
435        if (insertText !== undefined && !preferences.includeCompletionsWithInsertText) {
436            return undefined;
437        }
438
439        // TODO(drosen): Right now we just permit *all* semantic meanings when calling
440        // 'getSymbolKind' which is permissible given that it is backwards compatible; but
441        // really we should consider passing the meaning for the node so that we don't report
442        // that a suggestion for a value is an interface.  We COULD also just do what
443        // 'getSymbolModifiers' does, which is to use the first declaration.
444
445        // Use a 'sortText' of 0' so that all symbol completion entries come before any other
446        // entries (like JavaScript identifier entries).
447        return {
448            name,
449            kind: SymbolDisplay.getSymbolKind(typeChecker, symbol, location!), // TODO: GH#18217
450            kindModifiers: SymbolDisplay.getSymbolModifiers(typeChecker, symbol),
451            sortText,
452            source: getSourceFromOrigin(origin),
453            hasAction: origin && originIsExport(origin) || undefined,
454            isRecommended: isRecommendedCompletionMatch(symbol, recommendedCompletion, typeChecker) || undefined,
455            insertText,
456            replacementSpan,
457            isPackageJsonImport: originIsPackageJsonImport(origin) || undefined,
458        };
459    }
460
461    function quotePropertyName(sourceFile: SourceFile, preferences: UserPreferences, name: string,): string {
462        if (/^\d+$/.test(name)) {
463            return name;
464        }
465
466        return quote(sourceFile, preferences, name);
467    }
468
469    function isRecommendedCompletionMatch(localSymbol: Symbol, recommendedCompletion: Symbol | undefined, checker: TypeChecker): boolean {
470        return localSymbol === recommendedCompletion ||
471            !!(localSymbol.flags & SymbolFlags.ExportValue) && checker.getExportSymbolOfSymbol(localSymbol) === recommendedCompletion;
472    }
473
474    function getSourceFromOrigin(origin: SymbolOriginInfo | undefined): string | undefined {
475        if (originIsExport(origin)) {
476            return stripQuotes(origin.moduleSymbol.name);
477        }
478        if (origin?.kind === SymbolOriginInfoKind.ThisType) {
479            return CompletionSource.ThisProperty;
480        }
481    }
482
483    export function getCompletionEntriesFromSymbols(
484        symbols: readonly Symbol[],
485        entries: Push<CompletionEntry>,
486        contextToken: Node | undefined,
487        location: Node | undefined,
488        sourceFile: SourceFile,
489        typeChecker: TypeChecker,
490        target: ScriptTarget,
491        log: Log,
492        kind: CompletionKind,
493        preferences: UserPreferences,
494        propertyAccessToConvert?: PropertyAccessExpression,
495        jsxIdentifierExpected?: boolean,
496        isJsxInitializer?: IsJsxInitializer,
497        recommendedCompletion?: Symbol,
498        symbolToOriginInfoMap?: SymbolOriginInfoMap,
499        symbolToSortTextMap?: SymbolSortTextMap,
500    ): UniqueNameSet {
501        const start = timestamp();
502        // Tracks unique names.
503        // Value is set to false for global variables or completions from external module exports, because we can have multiple of those;
504        // true otherwise. Based on the order we add things we will always see locals first, then globals, then module exports.
505        // So adding a completion for a local will prevent us from adding completions for external module exports sharing the same name.
506        const uniques = new Map<string, boolean>();
507        for (const symbol of symbols) {
508            const origin = symbolToOriginInfoMap ? symbolToOriginInfoMap[getSymbolId(symbol)] : undefined;
509            const info = getCompletionEntryDisplayNameForSymbol(symbol, target, origin, kind, !!jsxIdentifierExpected);
510            if (!info) {
511                continue;
512            }
513            const { name, needsConvertPropertyAccess } = info;
514            if (uniques.get(name)) {
515                continue;
516            }
517
518            const entry = createCompletionEntry(
519                symbol,
520                symbolToSortTextMap && symbolToSortTextMap[getSymbolId(symbol)] || SortText.LocationPriority,
521                contextToken,
522                location,
523                sourceFile,
524                typeChecker,
525                name,
526                needsConvertPropertyAccess,
527                origin,
528                recommendedCompletion,
529                propertyAccessToConvert,
530                isJsxInitializer,
531                preferences
532            );
533            if (!entry) {
534                continue;
535            }
536
537            /** True for locals; false for globals, module exports from other files, `this.` completions. */
538            const shouldShadowLaterSymbols = !origin && !(symbol.parent === undefined && !some(symbol.declarations, d => d.getSourceFile() === location!.getSourceFile()));
539            uniques.set(name, shouldShadowLaterSymbols);
540            // add jsDoc info at interface getCompletionsAtPosition
541            if (symbol.getJsDocTags().length > 0) {
542                entry.jsDoc = symbol.getJsDocTags();
543            }
544            // add displayParts info at interface getCompletionsAtPosition
545            if (symbol.declarations) {
546                const symbolDisplayPartsDocumentationAndSymbolKind = SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker, symbol, sourceFile, location, location!, SemanticMeaning.All);
547                entry.displayParts = symbolDisplayPartsDocumentationAndSymbolKind.displayParts;
548            }
549            entries.push(entry);
550        }
551
552        log("getCompletionsAtPosition: getCompletionEntriesFromSymbols: " + (timestamp() - start));
553
554        // Prevent consumers of this map from having to worry about
555        // the boolean value. Externally, it should be seen as the
556        // set of all names.
557        return {
558            has: name => uniques.has(name),
559            add: name => uniques.set(name, true),
560        };
561    }
562
563    function getLabelCompletionAtPosition(node: BreakOrContinueStatement): CompletionInfo | undefined {
564        const entries = getLabelStatementCompletions(node);
565        if (entries.length) {
566            return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, entries };
567        }
568    }
569
570    function getLabelStatementCompletions(node: Node): CompletionEntry[] {
571        const entries: CompletionEntry[] = [];
572        const uniques = new Map<string, true>();
573        let current = node;
574
575        while (current) {
576            if (isFunctionLike(current)) {
577                break;
578            }
579            if (isLabeledStatement(current)) {
580                const name = current.label.text;
581                if (!uniques.has(name)) {
582                    uniques.set(name, true);
583                    entries.push({
584                        name,
585                        kindModifiers: ScriptElementKindModifier.none,
586                        kind: ScriptElementKind.label,
587                        sortText: SortText.LocationPriority
588                    });
589                }
590            }
591            current = current.parent;
592        }
593        return entries;
594    }
595
596    interface SymbolCompletion {
597        type: "symbol";
598        symbol: Symbol;
599        location: Node | undefined;
600        symbolToOriginInfoMap: SymbolOriginInfoMap;
601        previousToken: Node | undefined;
602        readonly isJsxInitializer: IsJsxInitializer;
603        readonly isTypeOnlyLocation: boolean;
604    }
605    function getSymbolCompletionFromEntryId(
606        program: Program,
607        log: Log,
608        sourceFile: SourceFile,
609        position: number,
610        entryId: CompletionEntryIdentifier,
611        host: LanguageServiceHost,
612        preferences: UserPreferences,
613    ): SymbolCompletion | { type: "request", request: Request } | { type: "literal", literal: string | number | PseudoBigInt } | { type: "none" } {
614        const compilerOptions = program.getCompilerOptions();
615        const completionData = getCompletionData(program, log, sourceFile, isUncheckedFile(sourceFile, compilerOptions), position, { includeCompletionsForModuleExports: true, includeCompletionsWithInsertText: true }, entryId, host);
616        if (!completionData) {
617            return { type: "none" };
618        }
619        if (completionData.kind !== CompletionDataKind.Data) {
620            return { type: "request", request: completionData };
621        }
622
623        const { symbols, literals, location, completionKind, symbolToOriginInfoMap, previousToken, isJsxInitializer, isTypeOnlyLocation } = completionData;
624
625        const literal = find(literals, l => completionNameForLiteral(sourceFile, preferences, l) === entryId.name);
626        if (literal !== undefined) return { type: "literal", literal };
627
628        // Find the symbol with the matching entry name.
629        // We don't need to perform character checks here because we're only comparing the
630        // name against 'entryName' (which is known to be good), not building a new
631        // completion entry.
632        return firstDefined(symbols, (symbol): SymbolCompletion | undefined => {
633            const origin = symbolToOriginInfoMap[getSymbolId(symbol)];
634            const info = getCompletionEntryDisplayNameForSymbol(symbol, compilerOptions.target!, origin, completionKind, completionData.isJsxIdentifierExpected);
635            return info && info.name === entryId.name && getSourceFromOrigin(origin) === entryId.source
636                ? { type: "symbol" as const, symbol, location, symbolToOriginInfoMap, previousToken, isJsxInitializer, isTypeOnlyLocation }
637                : undefined;
638        }) || { type: "none" };
639    }
640
641    export interface CompletionEntryIdentifier {
642        name: string;
643        source?: string;
644    }
645
646    export function getCompletionEntryDetails(
647        program: Program,
648        log: Log,
649        sourceFile: SourceFile,
650        position: number,
651        entryId: CompletionEntryIdentifier,
652        host: LanguageServiceHost,
653        formatContext: formatting.FormatContext,
654        preferences: UserPreferences,
655        cancellationToken: CancellationToken,
656    ): CompletionEntryDetails | undefined {
657        const typeChecker = program.getTypeChecker();
658        const compilerOptions = program.getCompilerOptions();
659        const { name } = entryId;
660
661        const contextToken = findPrecedingToken(position, sourceFile);
662        if (isInString(sourceFile, position, contextToken)) {
663            return StringCompletions.getStringLiteralCompletionDetails(name, sourceFile, position, contextToken, typeChecker, compilerOptions, host, cancellationToken);
664        }
665
666        // Compute all the completion symbols again.
667        const symbolCompletion = getSymbolCompletionFromEntryId(program, log, sourceFile, position, entryId, host, preferences);
668        switch (symbolCompletion.type) {
669            case "request": {
670                const { request } = symbolCompletion;
671                switch (request.kind) {
672                    case CompletionDataKind.JsDocTagName:
673                        return JsDoc.getJSDocTagNameCompletionDetails(name);
674                    case CompletionDataKind.JsDocTag:
675                        return JsDoc.getJSDocTagCompletionDetails(name);
676                    case CompletionDataKind.JsDocParameterName:
677                        return JsDoc.getJSDocParameterNameCompletionDetails(name);
678                    default:
679                        return Debug.assertNever(request);
680                }
681            }
682            case "symbol": {
683                const { symbol, location, symbolToOriginInfoMap, previousToken } = symbolCompletion;
684                const { codeActions, sourceDisplay } = getCompletionEntryCodeActionsAndSourceDisplay(symbolToOriginInfoMap, symbol, program, typeChecker, host, compilerOptions, sourceFile, position, previousToken, formatContext, preferences);
685                return createCompletionDetailsForSymbol(symbol, typeChecker, sourceFile, location!, cancellationToken, codeActions, sourceDisplay); // TODO: GH#18217
686            }
687            case "literal": {
688                const { literal } = symbolCompletion;
689                return createSimpleDetails(completionNameForLiteral(sourceFile, preferences, literal), ScriptElementKind.string, typeof literal === "string" ? SymbolDisplayPartKind.stringLiteral : SymbolDisplayPartKind.numericLiteral);
690            }
691            case "none":
692                // Didn't find a symbol with this name.  See if we can find a keyword instead.
693                return allKeywordsCompletions().some(c => c.name === name) ? createSimpleDetails(name, ScriptElementKind.keyword, SymbolDisplayPartKind.keyword) : undefined;
694            default:
695                Debug.assertNever(symbolCompletion);
696        }
697    }
698
699    function createSimpleDetails(name: string, kind: ScriptElementKind, kind2: SymbolDisplayPartKind): CompletionEntryDetails {
700        return createCompletionDetails(name, ScriptElementKindModifier.none, kind, [displayPart(name, kind2)]);
701    }
702
703    export function createCompletionDetailsForSymbol(symbol: Symbol, checker: TypeChecker, sourceFile: SourceFile, location: Node, cancellationToken: CancellationToken, codeActions?: CodeAction[], sourceDisplay?: SymbolDisplayPart[]): CompletionEntryDetails {
704        const { displayParts, documentation, symbolKind, tags } =
705            checker.runWithCancellationToken(cancellationToken, checker =>
706                SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(checker, symbol, sourceFile, location, location, SemanticMeaning.All)
707            );
708        return createCompletionDetails(symbol.name, SymbolDisplay.getSymbolModifiers(checker, symbol), symbolKind, displayParts, documentation, tags, codeActions, sourceDisplay);
709    }
710
711    export function createCompletionDetails(name: string, kindModifiers: string, kind: ScriptElementKind, displayParts: SymbolDisplayPart[], documentation?: SymbolDisplayPart[], tags?: JSDocTagInfo[], codeActions?: CodeAction[], source?: SymbolDisplayPart[]): CompletionEntryDetails {
712        return { name, kindModifiers, kind, displayParts, documentation, tags, codeActions, source };
713    }
714
715    interface CodeActionsAndSourceDisplay {
716        readonly codeActions: CodeAction[] | undefined;
717        readonly sourceDisplay: SymbolDisplayPart[] | undefined;
718    }
719    function getCompletionEntryCodeActionsAndSourceDisplay(
720        symbolToOriginInfoMap: SymbolOriginInfoMap,
721        symbol: Symbol,
722        program: Program,
723        checker: TypeChecker,
724        host: LanguageServiceHost,
725        compilerOptions: CompilerOptions,
726        sourceFile: SourceFile,
727        position: number,
728        previousToken: Node | undefined,
729        formatContext: formatting.FormatContext,
730        preferences: UserPreferences,
731    ): CodeActionsAndSourceDisplay {
732        const symbolOriginInfo = symbolToOriginInfoMap[getSymbolId(symbol)];
733        if (!symbolOriginInfo || !originIsExport(symbolOriginInfo)) {
734            return { codeActions: undefined, sourceDisplay: undefined };
735        }
736
737        const { moduleSymbol } = symbolOriginInfo;
738        const exportedSymbol = checker.getMergedSymbol(skipAlias(symbol.exportSymbol || symbol, checker));
739        const { moduleSpecifier, codeAction } = codefix.getImportCompletionAction(
740            exportedSymbol,
741            moduleSymbol,
742            sourceFile,
743            getNameForExportedSymbol(symbol, compilerOptions.target),
744            host,
745            program,
746            formatContext,
747            previousToken && isIdentifier(previousToken) ? previousToken.getStart(sourceFile) : position,
748            preferences);
749        return { sourceDisplay: [textPart(moduleSpecifier)], codeActions: [codeAction] };
750    }
751
752    export function getCompletionEntrySymbol(
753        program: Program,
754        log: Log,
755        sourceFile: SourceFile,
756        position: number,
757        entryId: CompletionEntryIdentifier,
758        host: LanguageServiceHost,
759        preferences: UserPreferences,
760    ): Symbol | undefined {
761        const completion = getSymbolCompletionFromEntryId(program, log, sourceFile, position, entryId, host, preferences);
762        return completion.type === "symbol" ? completion.symbol : undefined;
763    }
764
765    const enum CompletionDataKind { Data, JsDocTagName, JsDocTag, JsDocParameterName }
766    /** true: after the `=` sign but no identifier has been typed yet. Else is the Identifier after the initializer. */
767    type IsJsxInitializer = boolean | Identifier;
768    interface CompletionData {
769        readonly kind: CompletionDataKind.Data;
770        readonly symbols: readonly Symbol[];
771        readonly completionKind: CompletionKind;
772        readonly isInSnippetScope: boolean;
773        /** Note that the presence of this alone doesn't mean that we need a conversion. Only do that if the completion is not an ordinary identifier. */
774        readonly propertyAccessToConvert: PropertyAccessExpression | undefined;
775        readonly isNewIdentifierLocation: boolean;
776        readonly location: Node | undefined;
777        readonly keywordFilters: KeywordCompletionFilters;
778        readonly literals: readonly (string | number | PseudoBigInt)[];
779        readonly symbolToOriginInfoMap: SymbolOriginInfoMap;
780        readonly recommendedCompletion: Symbol | undefined;
781        readonly previousToken: Node | undefined;
782        readonly isJsxInitializer: IsJsxInitializer;
783        readonly insideJsDocTagTypeExpression: boolean;
784        readonly symbolToSortTextMap: SymbolSortTextMap;
785        readonly isTypeOnlyLocation: boolean;
786        /** In JSX tag name and attribute names, identifiers like "my-tag" or "aria-name" is valid identifier. */
787        readonly isJsxIdentifierExpected: boolean;
788    }
789    type Request = { readonly kind: CompletionDataKind.JsDocTagName | CompletionDataKind.JsDocTag } | { readonly kind: CompletionDataKind.JsDocParameterName, tag: JSDocParameterTag };
790
791    export const enum CompletionKind {
792        ObjectPropertyDeclaration,
793        Global,
794        PropertyAccess,
795        MemberLike,
796        String,
797        None,
798    }
799
800    function getRecommendedCompletion(previousToken: Node, contextualType: Type, checker: TypeChecker): Symbol | undefined {
801        // For a union, return the first one with a recommended completion.
802        return firstDefined(contextualType && (contextualType.isUnion() ? contextualType.types : [contextualType]), type => {
803            const symbol = type && type.symbol;
804            // Don't include make a recommended completion for an abstract class
805            return symbol && (symbol.flags & (SymbolFlags.EnumMember | SymbolFlags.Enum | SymbolFlags.Class) && !isAbstractConstructorSymbol(symbol))
806                ? getFirstSymbolInChain(symbol, previousToken, checker)
807                : undefined;
808        });
809    }
810
811    function getContextualType(previousToken: Node, position: number, sourceFile: SourceFile, checker: TypeChecker): Type | undefined {
812        const { parent } = previousToken;
813        switch (previousToken.kind) {
814            case SyntaxKind.Identifier:
815                return getContextualTypeFromParent(previousToken as Identifier, checker);
816            case SyntaxKind.EqualsToken:
817                switch (parent.kind) {
818                    case SyntaxKind.VariableDeclaration:
819                        return checker.getContextualType((parent as VariableDeclaration).initializer!); // TODO: GH#18217
820                    case SyntaxKind.BinaryExpression:
821                        return checker.getTypeAtLocation((parent as BinaryExpression).left);
822                    case SyntaxKind.JsxAttribute:
823                        return checker.getContextualTypeForJsxAttribute(parent as JsxAttribute);
824                    default:
825                        return undefined;
826                }
827            case SyntaxKind.NewKeyword:
828                return checker.getContextualType(parent as Expression);
829            case SyntaxKind.CaseKeyword:
830                return getSwitchedType(cast(parent, isCaseClause), checker);
831            case SyntaxKind.OpenBraceToken:
832                return isJsxExpression(parent) && parent.parent.kind !== SyntaxKind.JsxElement ? checker.getContextualTypeForJsxAttribute(parent.parent) : undefined;
833            default:
834                const argInfo = SignatureHelp.getArgumentInfoForCompletions(previousToken, position, sourceFile);
835                return argInfo ?
836                    // At `,`, treat this as the next argument after the comma.
837                    checker.getContextualTypeForArgumentAtIndex(argInfo.invocation, argInfo.argumentIndex + (previousToken.kind === SyntaxKind.CommaToken ? 1 : 0)) :
838                    isEqualityOperatorKind(previousToken.kind) && isBinaryExpression(parent) && isEqualityOperatorKind(parent.operatorToken.kind) ?
839                        // completion at `x ===/**/` should be for the right side
840                        checker.getTypeAtLocation(parent.left) :
841                        checker.getContextualType(previousToken as Expression);
842        }
843    }
844
845    function getFirstSymbolInChain(symbol: Symbol, enclosingDeclaration: Node, checker: TypeChecker): Symbol | undefined {
846        const chain = checker.getAccessibleSymbolChain(symbol, enclosingDeclaration, /*meaning*/ SymbolFlags.All, /*useOnlyExternalAliasing*/ false);
847        if (chain) return first(chain);
848        return symbol.parent && (isModuleSymbol(symbol.parent) ? symbol : getFirstSymbolInChain(symbol.parent, enclosingDeclaration, checker));
849    }
850
851    function isModuleSymbol(symbol: Symbol): boolean {
852        return symbol.declarations.some(d => d.kind === SyntaxKind.SourceFile);
853    }
854
855    function getCompletionData(
856        program: Program,
857        log: (message: string) => void,
858        sourceFile: SourceFile,
859        isUncheckedFile: boolean,
860        position: number,
861        preferences: Pick<UserPreferences, "includeCompletionsForModuleExports" | "includeCompletionsWithInsertText" | "includeAutomaticOptionalChainCompletions">,
862        detailsEntryId: CompletionEntryIdentifier | undefined,
863        host: LanguageServiceHost
864    ): CompletionData | Request | undefined {
865        const isEtsFile = sourceFile.scriptKind === ScriptKind.ETS;
866        const typeChecker = program.getTypeChecker();
867        const compilerOptions = program.getCompilerOptions();
868
869        let start = timestamp();
870        let currentToken = getTokenAtPosition(sourceFile, position); // TODO: GH#15853
871        // We will check for jsdoc comments with insideComment and getJsDocTagAtPosition. (TODO: that seems rather inefficient to check the same thing so many times.)
872
873        log("getCompletionData: Get current token: " + (timestamp() - start));
874
875        start = timestamp();
876        const insideComment = isInComment(sourceFile, position, currentToken);
877        log("getCompletionData: Is inside comment: " + (timestamp() - start));
878
879        let insideJsDocTagTypeExpression = false;
880        let isInSnippetScope = false;
881        if (insideComment) {
882            if (hasDocComment(sourceFile, position)) {
883                if (sourceFile.text.charCodeAt(position - 1) === CharacterCodes.at) {
884                    // The current position is next to the '@' sign, when no tag name being provided yet.
885                    // Provide a full list of tag names
886                    return { kind: CompletionDataKind.JsDocTagName };
887                }
888                else {
889                    // When completion is requested without "@", we will have check to make sure that
890                    // there are no comments prefix the request position. We will only allow "*" and space.
891                    // e.g
892                    //   /** |c| /*
893                    //
894                    //   /**
895                    //     |c|
896                    //    */
897                    //
898                    //   /**
899                    //    * |c|
900                    //    */
901                    //
902                    //   /**
903                    //    *         |c|
904                    //    */
905                    const lineStart = getLineStartPositionForPosition(position, sourceFile);
906                    if (!/[^\*|\s(/)]/.test(sourceFile.text.substring(lineStart, position))) {
907                        return { kind: CompletionDataKind.JsDocTag };
908                    }
909                }
910            }
911
912            // Completion should work inside certain JsDoc tags. For example:
913            //     /** @type {number | string} */
914            // Completion should work in the brackets
915            const tag = getJsDocTagAtPosition(currentToken, position);
916            if (tag) {
917                if (tag.tagName.pos <= position && position <= tag.tagName.end) {
918                    return { kind: CompletionDataKind.JsDocTagName };
919                }
920                if (isTagWithTypeExpression(tag) && tag.typeExpression && tag.typeExpression.kind === SyntaxKind.JSDocTypeExpression) {
921                    currentToken = getTokenAtPosition(sourceFile, position);
922                    if (!currentToken ||
923                        (!isDeclarationName(currentToken) &&
924                            (currentToken.parent.kind !== SyntaxKind.JSDocPropertyTag ||
925                                (<JSDocPropertyTag>currentToken.parent).name !== currentToken))) {
926                        // Use as type location if inside tag's type expression
927                        insideJsDocTagTypeExpression = isCurrentlyEditingNode(tag.typeExpression);
928                    }
929                }
930                if (!insideJsDocTagTypeExpression && isJSDocParameterTag(tag) && (nodeIsMissing(tag.name) || tag.name.pos <= position && position <= tag.name.end)) {
931                    return { kind: CompletionDataKind.JsDocParameterName, tag };
932                }
933            }
934
935            if (!insideJsDocTagTypeExpression) {
936                // Proceed if the current position is in jsDoc tag expression; otherwise it is a normal
937                // comment or the plain text part of a jsDoc comment, so no completion should be available
938                log("Returning an empty list because completion was inside a regular comment or plain text part of a JsDoc comment.");
939                return undefined;
940            }
941        }
942
943        start = timestamp();
944        const previousToken = findPrecedingToken(position, sourceFile, /*startNode*/ undefined)!; // TODO: GH#18217
945        log("getCompletionData: Get previous token 1: " + (timestamp() - start));
946
947        // The decision to provide completion depends on the contextToken, which is determined through the previousToken.
948        // Note: 'previousToken' (and thus 'contextToken') can be undefined if we are the beginning of the file
949        let contextToken = previousToken;
950
951        // Check if the caret is at the end of an identifier; this is a partial identifier that we want to complete: e.g. a.toS|
952        // Skip this partial identifier and adjust the contextToken to the token that precedes it.
953        if (contextToken && position <= contextToken.end && (isIdentifierOrPrivateIdentifier(contextToken) || isKeyword(contextToken.kind))) {
954            const start = timestamp();
955            contextToken = findPrecedingToken(contextToken.getFullStart(), sourceFile, /*startNode*/ undefined)!; // TODO: GH#18217
956            log("getCompletionData: Get previous token 2: " + (timestamp() - start));
957        }
958
959        // Find the node where completion is requested on.
960        // Also determine whether we are trying to complete with members of that node
961        // or attributes of a JSX tag.
962        let node = currentToken;
963        let propertyAccessToConvert: PropertyAccessExpression | undefined;
964        let isRightOfDot = false;
965        let isRightOfQuestionDot = false;
966        let isRightOfOpenTag = false;
967        let isStartingCloseTag = false;
968        let isJsxInitializer: IsJsxInitializer = false;
969        let isJsxIdentifierExpected = false;
970
971        let location = getTouchingPropertyName(sourceFile, position);
972        if (contextToken) {
973            // Bail out if this is a known invalid completion location
974            if (isCompletionListBlocker(contextToken)) {
975                log("Returning an empty list because completion was requested in an invalid position.");
976                return undefined;
977            }
978
979            let parent = contextToken.parent;
980            if (contextToken.kind === SyntaxKind.DotToken || contextToken.kind === SyntaxKind.QuestionDotToken) {
981                isRightOfDot = contextToken.kind === SyntaxKind.DotToken;
982                isRightOfQuestionDot = contextToken.kind === SyntaxKind.QuestionDotToken;
983                switch (parent.kind) {
984                    case SyntaxKind.PropertyAccessExpression:
985                        propertyAccessToConvert = parent as PropertyAccessExpression;
986                        node = propertyAccessToConvert.expression;
987                        if ((isCallExpression(node) || isFunctionLike(node) || isEtsComponentExpression(node)) &&
988                            node.end === contextToken.pos &&
989                            node.getChildCount(sourceFile) &&
990                            last(node.getChildren(sourceFile)).kind !== SyntaxKind.CloseParenToken && !node.getLastToken(sourceFile)) {
991                            // This is likely dot from incorrectly parsed expression and user is starting to write spread
992                            // eg: Math.min(./**/)
993                            // const x = function (./**/) {}
994                            return undefined;
995                        }
996                        if (node.virtual && findPrecedingToken(node.pos, sourceFile)?.kind === SyntaxKind.OpenParenToken) {
997                            return undefined;
998                        }
999                        break;
1000                    case SyntaxKind.QualifiedName:
1001                        node = (parent as QualifiedName).left;
1002                        break;
1003                    case SyntaxKind.ModuleDeclaration:
1004                        node = (parent as ModuleDeclaration).name;
1005                        break;
1006                    case SyntaxKind.ImportType:
1007                    case SyntaxKind.MetaProperty:
1008                        node = parent;
1009                        break;
1010                    default:
1011                        // There is nothing that precedes the dot, so this likely just a stray character
1012                        // or leading into a '...' token. Just bail out instead.
1013                        return undefined;
1014                }
1015            }
1016            else if (sourceFile.languageVariant === LanguageVariant.JSX) {
1017                // <UI.Test /* completion position */ />
1018                // If the tagname is a property access expression, we will then walk up to the top most of property access expression.
1019                // Then, try to get a JSX container and its associated attributes type.
1020                if (parent && parent.kind === SyntaxKind.PropertyAccessExpression) {
1021                    contextToken = parent;
1022                    parent = parent.parent;
1023                }
1024
1025                // Fix location
1026                if (currentToken.parent === location) {
1027                    switch (currentToken.kind) {
1028                        case SyntaxKind.GreaterThanToken:
1029                            if (currentToken.parent.kind === SyntaxKind.JsxElement || currentToken.parent.kind === SyntaxKind.JsxOpeningElement) {
1030                                location = currentToken;
1031                            }
1032                            break;
1033
1034                        case SyntaxKind.SlashToken:
1035                            if (currentToken.parent.kind === SyntaxKind.JsxSelfClosingElement) {
1036                                location = currentToken;
1037                            }
1038                            break;
1039                    }
1040                }
1041
1042                switch (parent.kind) {
1043                    case SyntaxKind.JsxClosingElement:
1044                        if (contextToken.kind === SyntaxKind.SlashToken) {
1045                            isStartingCloseTag = true;
1046                            location = contextToken;
1047                        }
1048                        break;
1049
1050                    case SyntaxKind.BinaryExpression:
1051                        if (!binaryExpressionMayBeOpenTag(parent as BinaryExpression)) {
1052                            break;
1053                        }
1054                    // falls through
1055
1056                    case SyntaxKind.JsxSelfClosingElement:
1057                    case SyntaxKind.JsxElement:
1058                    case SyntaxKind.JsxOpeningElement:
1059                        isJsxIdentifierExpected = true;
1060                        if (contextToken.kind === SyntaxKind.LessThanToken) {
1061                            isRightOfOpenTag = true;
1062                            location = contextToken;
1063                        }
1064                        break;
1065
1066                    case SyntaxKind.JsxExpression:
1067                        // For `<div foo={true} [||] ></div>`, `parent` will be `{true}` and `previousToken` will be `}`
1068                        if (previousToken.kind === SyntaxKind.CloseBraceToken && currentToken.kind === SyntaxKind.GreaterThanToken) {
1069                            isJsxIdentifierExpected = true;
1070                        }
1071                        break;
1072
1073                    case SyntaxKind.JsxAttribute:
1074                        // For `<div className="x" [||] ></div>`, `parent` will be JsxAttribute and `previousToken` will be its initializer
1075                        if ((parent as JsxAttribute).initializer === previousToken &&
1076                            previousToken.end < position) {
1077                            isJsxIdentifierExpected = true;
1078                            break;
1079                        }
1080                        switch (previousToken.kind) {
1081                            case SyntaxKind.EqualsToken:
1082                                isJsxInitializer = true;
1083                                break;
1084                            case SyntaxKind.Identifier:
1085                                isJsxIdentifierExpected = true;
1086                                // For `<div x=[|f/**/|]`, `parent` will be `x` and `previousToken.parent` will be `f` (which is its own JsxAttribute)
1087                                // Note for `<div someBool f>` we don't want to treat this as a jsx inializer, instead it's the attribute name.
1088                                if (parent !== previousToken.parent &&
1089                                    !(parent as JsxAttribute).initializer &&
1090                                    findChildOfKind(parent, SyntaxKind.EqualsToken, sourceFile)) {
1091                                    isJsxInitializer = previousToken as Identifier;
1092                                }
1093                        }
1094                        break;
1095                }
1096            }
1097        }
1098
1099        const semanticStart = timestamp();
1100        let completionKind = CompletionKind.None;
1101        let isNewIdentifierLocation = false;
1102        let isNonContextualObjectLiteral = false;
1103        let keywordFilters = KeywordCompletionFilters.None;
1104        // This also gets mutated in nested-functions after the return
1105        let symbols: Symbol[] = [];
1106        const symbolToOriginInfoMap: SymbolOriginInfoMap = [];
1107        const symbolToSortTextMap: SymbolSortTextMap = [];
1108        const importSuggestionsCache = host.getImportSuggestionsCache && host.getImportSuggestionsCache();
1109        const isTypeOnly = isTypeOnlyCompletion();
1110
1111        if (isRightOfDot || isRightOfQuestionDot) {
1112            getTypeScriptMemberSymbols();
1113        }
1114        else if (isRightOfOpenTag) {
1115            const tagSymbols = typeChecker.getJsxIntrinsicTagNamesAt(location);
1116            Debug.assertEachIsDefined(tagSymbols, "getJsxIntrinsicTagNames() should all be defined");
1117            tryGetGlobalSymbols();
1118            symbols = tagSymbols.concat(symbols);
1119            completionKind = CompletionKind.MemberLike;
1120            keywordFilters = KeywordCompletionFilters.None;
1121        }
1122        else if (isStartingCloseTag) {
1123            const tagName = (<JsxElement>contextToken.parent.parent).openingElement.tagName;
1124            const tagSymbol = typeChecker.getSymbolAtLocation(tagName);
1125            if (tagSymbol) {
1126                symbols = [tagSymbol];
1127            }
1128            completionKind = CompletionKind.MemberLike;
1129            keywordFilters = KeywordCompletionFilters.None;
1130        }
1131        else {
1132            // For JavaScript or TypeScript, if we're not after a dot, then just try to get the
1133            // global symbols in scope.  These results should be valid for either language as
1134            // the set of symbols that can be referenced from this location.
1135            if (!tryGetGlobalSymbols()) {
1136                return undefined;
1137            }
1138        }
1139
1140        const etsLibFilesNames = program.getEtsLibSFromProgram();
1141        symbols = symbols.filter(symbol => {
1142            if(!symbol.declarations || !symbol.declarations.length) {
1143                return true;
1144            }
1145            const declaration = (symbol.declarations??[]).filter(declaration =>{
1146                if(!declaration.getSourceFile().fileName) {
1147                    return true;
1148                }
1149                const symbolFileName = sys.resolvePath(declaration.getSourceFile().fileName);
1150                if(!isEtsFile && etsLibFilesNames.indexOf(symbolFileName) !== -1) {
1151                    return false;
1152                }
1153                return true;
1154            });
1155            return declaration.length;
1156        });
1157
1158        log("getCompletionData: Semantic work: " + (timestamp() - semanticStart));
1159        const contextualType = previousToken && getContextualType(previousToken, position, sourceFile, typeChecker);
1160        const literals = mapDefined(contextualType && (contextualType.isUnion() ? contextualType.types : [contextualType]), t => t.isLiteral() ? t.value : undefined);
1161
1162        const recommendedCompletion = previousToken && contextualType && getRecommendedCompletion(previousToken, contextualType, typeChecker);
1163        return {
1164            kind: CompletionDataKind.Data,
1165            symbols,
1166            completionKind,
1167            isInSnippetScope,
1168            propertyAccessToConvert,
1169            isNewIdentifierLocation,
1170            location,
1171            keywordFilters,
1172            literals,
1173            symbolToOriginInfoMap,
1174            recommendedCompletion,
1175            previousToken,
1176            isJsxInitializer,
1177            insideJsDocTagTypeExpression,
1178            symbolToSortTextMap,
1179            isTypeOnlyLocation: isTypeOnly,
1180            isJsxIdentifierExpected,
1181        };
1182
1183        type JSDocTagWithTypeExpression = JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag;
1184
1185        function isTagWithTypeExpression(tag: JSDocTag): tag is JSDocTagWithTypeExpression {
1186            switch (tag.kind) {
1187                case SyntaxKind.JSDocParameterTag:
1188                case SyntaxKind.JSDocPropertyTag:
1189                case SyntaxKind.JSDocReturnTag:
1190                case SyntaxKind.JSDocTypeTag:
1191                case SyntaxKind.JSDocTypedefTag:
1192                    return true;
1193                default:
1194                    return false;
1195            }
1196        }
1197
1198        function getTypeScriptMemberSymbols(): void {
1199            // Right of dot member completion list
1200            completionKind = CompletionKind.PropertyAccess;
1201
1202            // Since this is qualified name check it's a type node location
1203            const isImportType = isLiteralImportTypeNode(node);
1204            const isTypeLocation = insideJsDocTagTypeExpression
1205                || (isImportType && !(node as ImportTypeNode).isTypeOf)
1206                || isPartOfTypeNode(node.parent)
1207                || isPossiblyTypeArgumentPosition(contextToken, sourceFile, typeChecker);
1208            const isRhsOfImportDeclaration = isInRightSideOfInternalImportEqualsDeclaration(node);
1209            if (isEntityName(node) || isImportType || isPropertyAccessExpression(node)) {
1210                const isNamespaceName = isModuleDeclaration(node.parent);
1211                if (isNamespaceName) isNewIdentifierLocation = true;
1212                let symbol = typeChecker.getSymbolAtLocation(node);
1213                if (symbol) {
1214                    symbol = skipAlias(symbol, typeChecker);
1215                    if (symbol.flags & (SymbolFlags.Module | SymbolFlags.Enum)) {
1216                        // Extract module or enum members
1217                        const exportedSymbols = typeChecker.getExportsOfModule(symbol);
1218                        Debug.assertEachIsDefined(exportedSymbols, "getExportsOfModule() should all be defined");
1219                        const isValidValueAccess = (symbol: Symbol) => typeChecker.isValidPropertyAccess(isImportType ? <ImportTypeNode>node : <PropertyAccessExpression>(node.parent), symbol.name);
1220                        const isValidTypeAccess = (symbol: Symbol) => symbolCanBeReferencedAtTypeLocation(symbol);
1221                        const isValidAccess: (symbol: Symbol) => boolean =
1222                            isNamespaceName
1223                                // At `namespace N.M/**/`, if this is the only declaration of `M`, don't include `M` as a completion.
1224                                ? symbol => !!(symbol.flags & SymbolFlags.Namespace) && !symbol.declarations.every(d => d.parent === node.parent)
1225                                : isRhsOfImportDeclaration ?
1226                                    // Any kind is allowed when dotting off namespace in internal import equals declaration
1227                                    symbol => isValidTypeAccess(symbol) || isValidValueAccess(symbol) :
1228                                    isTypeLocation ? isValidTypeAccess : isValidValueAccess;
1229                        for (const exportedSymbol of exportedSymbols) {
1230                            if (isValidAccess(exportedSymbol)) {
1231                                symbols.push(exportedSymbol);
1232                            }
1233                        }
1234
1235                        // If the module is merged with a value, we must get the type of the class and add its propertes (for inherited static methods).
1236                        if (!isTypeLocation &&
1237                            symbol.declarations &&
1238                            symbol.declarations.some(d => d.kind !== SyntaxKind.SourceFile && d.kind !== SyntaxKind.ModuleDeclaration && d.kind !== SyntaxKind.EnumDeclaration)) {
1239                            let type = typeChecker.getTypeOfSymbolAtLocation(symbol, node).getNonOptionalType();
1240                            let insertQuestionDot = false;
1241                            if (type.isNullableType()) {
1242                                const canCorrectToQuestionDot =
1243                                    isRightOfDot &&
1244                                    !isRightOfQuestionDot &&
1245                                    preferences.includeAutomaticOptionalChainCompletions !== false;
1246
1247                                if (canCorrectToQuestionDot || isRightOfQuestionDot) {
1248                                    type = type.getNonNullableType();
1249                                    if (canCorrectToQuestionDot) {
1250                                        insertQuestionDot = true;
1251                                    }
1252                                }
1253                            }
1254                            addTypeProperties(type, !!(node.flags & NodeFlags.AwaitContext), insertQuestionDot);
1255                        }
1256
1257                        return;
1258                    }
1259                }
1260            }
1261
1262            if (isMetaProperty(node) && (node.keywordToken === SyntaxKind.NewKeyword || node.keywordToken === SyntaxKind.ImportKeyword) && contextToken === node.getChildAt(1)) {
1263                const completion = (node.keywordToken === SyntaxKind.NewKeyword) ? "target" : "meta";
1264                symbols.push(typeChecker.createSymbol(SymbolFlags.Property, escapeLeadingUnderscores(completion)));
1265                return;
1266            }
1267
1268            if (!isTypeLocation) {
1269                let type = typeChecker.tryGetTypeAtLocationWithoutCheck(node).getNonOptionalType();
1270                let insertQuestionDot = false;
1271                if (type.isNullableType()) {
1272                    const canCorrectToQuestionDot =
1273                        isRightOfDot &&
1274                        !isRightOfQuestionDot &&
1275                        preferences.includeAutomaticOptionalChainCompletions !== false;
1276
1277                    if (canCorrectToQuestionDot || isRightOfQuestionDot) {
1278                        type = type.getNonNullableType();
1279                        if (canCorrectToQuestionDot) {
1280                            insertQuestionDot = true;
1281                        }
1282                    }
1283                }
1284                addTypeProperties(type, !!(node.flags & NodeFlags.AwaitContext), insertQuestionDot);
1285            }
1286        }
1287
1288        function addTypeProperties(type: Type, insertAwait: boolean, insertQuestionDot: boolean): void {
1289            isNewIdentifierLocation = !!type.getStringIndexType();
1290            if (isRightOfQuestionDot && some(type.getCallSignatures())) {
1291                isNewIdentifierLocation = true;
1292            }
1293
1294            const propertyAccess = node.kind === SyntaxKind.ImportType ? <ImportTypeNode>node : <PropertyAccessExpression | QualifiedName>node.parent;
1295            if (isUncheckedFile) {
1296                // In javascript files, for union types, we don't just get the members that
1297                // the individual types have in common, we also include all the members that
1298                // each individual type has. This is because we're going to add all identifiers
1299                // anyways. So we might as well elevate the members that were at least part
1300                // of the individual types to a higher status since we know what they are.
1301                symbols.push(...filter(getPropertiesForCompletion(type, typeChecker), s => typeChecker.isValidPropertyAccessForCompletions(propertyAccess, type, s)));
1302            }
1303            else {
1304                const typeSymbols = type.getApparentProperties();
1305
1306                for (const symbol of typeSymbols) {
1307                    if (typeChecker.isValidPropertyAccessForCompletions(propertyAccess, type, symbol)) {
1308                        addPropertySymbol(symbol, /* insertAwait */ false, insertQuestionDot);
1309                    }
1310                }
1311
1312                // The extension method on the ETS depends on whether the type is correctly parsed.
1313                if (typeSymbols.length) {
1314                    // if complete expression is ets component expression, then complete data need add extend properties and styles properties.
1315                    const etsComponentExpressionNode = getEtsComponentExpressionInnerCallExpressionNode(node)
1316                        || getRootEtsComponentInnerCallExpressionNode(node);
1317                    if (etsComponentExpressionNode) {
1318                        addEtsExtendPropertySymbol(etsComponentExpressionNode, insertQuestionDot);
1319                        addEtsStylesPropertySymbol(etsComponentExpressionNode, insertQuestionDot);
1320                    }
1321                }
1322            }
1323
1324            if (insertAwait && preferences.includeCompletionsWithInsertText) {
1325                const promiseType = typeChecker.getPromisedTypeOfPromise(type);
1326                if (promiseType) {
1327                    for (const symbol of promiseType.getApparentProperties()) {
1328                        if (typeChecker.isValidPropertyAccessForCompletions(propertyAccess, promiseType, symbol)) {
1329                            addPropertySymbol(symbol, /* insertAwait */ true, insertQuestionDot);
1330                        }
1331                    }
1332                }
1333            }
1334        }
1335
1336        function addPropertySymbol(symbol: Symbol, insertAwait: boolean, insertQuestionDot: boolean) {
1337            // For a computed property with an accessible name like `Symbol.iterator`,
1338            // we'll add a completion for the *name* `Symbol` instead of for the property.
1339            // If this is e.g. [Symbol.iterator], add a completion for `Symbol`.
1340            const computedPropertyName = firstDefined(symbol.declarations, decl => tryCast(getNameOfDeclaration(decl), isComputedPropertyName));
1341            if (computedPropertyName) {
1342                const leftMostName = getLeftMostName(computedPropertyName.expression); // The completion is for `Symbol`, not `iterator`.
1343                const nameSymbol = leftMostName && typeChecker.getSymbolAtLocation(leftMostName);
1344                // If this is nested like for `namespace N { export const sym = Symbol(); }`, we'll add the completion for `N`.
1345                const firstAccessibleSymbol = nameSymbol && getFirstSymbolInChain(nameSymbol, contextToken, typeChecker);
1346                if (firstAccessibleSymbol && !symbolToOriginInfoMap[getSymbolId(firstAccessibleSymbol)]) {
1347                    symbols.push(firstAccessibleSymbol);
1348                    const moduleSymbol = firstAccessibleSymbol.parent;
1349                    symbolToOriginInfoMap[getSymbolId(firstAccessibleSymbol)] =
1350                        !moduleSymbol || !isExternalModuleSymbol(moduleSymbol)
1351                            ? { kind: getNullableSymbolOriginInfoKind(SymbolOriginInfoKind.SymbolMemberNoExport) }
1352                            : { kind: getNullableSymbolOriginInfoKind(SymbolOriginInfoKind.SymbolMemberExport), moduleSymbol, isDefaultExport: false };
1353                }
1354                else if (preferences.includeCompletionsWithInsertText) {
1355                    addSymbolOriginInfo(symbol);
1356                    addSymbolSortInfo(symbol);
1357                    symbols.push(symbol);
1358                }
1359            }
1360            else {
1361                addSymbolOriginInfo(symbol);
1362                addSymbolSortInfo(symbol);
1363                symbols.push(symbol);
1364            }
1365
1366            function addSymbolSortInfo(symbol: Symbol) {
1367                if (isStaticProperty(symbol)) {
1368                    symbolToSortTextMap[getSymbolId(symbol)] = SortText.LocalDeclarationPriority;
1369                }
1370            }
1371
1372            function addSymbolOriginInfo(symbol: Symbol) {
1373                if (preferences.includeCompletionsWithInsertText) {
1374                    if (insertAwait && !symbolToOriginInfoMap[getSymbolId(symbol)]) {
1375                        symbolToOriginInfoMap[getSymbolId(symbol)] = { kind: getNullableSymbolOriginInfoKind(SymbolOriginInfoKind.Promise) };
1376                    }
1377                    else if (insertQuestionDot) {
1378                        symbolToOriginInfoMap[getSymbolId(symbol)] = { kind: SymbolOriginInfoKind.Nullable };
1379                    }
1380                }
1381            }
1382
1383            function getNullableSymbolOriginInfoKind(kind: SymbolOriginInfoKind) {
1384                return insertQuestionDot ? kind | SymbolOriginInfoKind.Nullable : kind;
1385            }
1386        }
1387
1388        function addEtsExtendPropertySymbol(node: EtsComponentExpression, insertQuestionDot: boolean) {
1389            const locals = getSourceFileOfNode(node).locals;
1390            if (!locals) {
1391                return;
1392            }
1393            const etsComponentName = node.expression.kind === SyntaxKind.Identifier ? (<Identifier>node.expression).escapedText : undefined;
1394            const extendComponentSymbolMap: UnderscoreEscapedMap<Symbol[]> = new Map<__String, Symbol[]>();
1395            locals.forEach(local => {
1396                getEtsExtendDecoratorComponentNames(local.valueDeclaration?.decorators, compilerOptions).forEach((extendName) => {
1397                    if (extendComponentSymbolMap.has(extendName)) {
1398                        extendComponentSymbolMap.get(extendName)!.push(local);
1399                    }
1400                    else {
1401                        extendComponentSymbolMap.set(extendName, [local]);
1402                    }
1403                });
1404            });
1405
1406            if (etsComponentName && extendComponentSymbolMap.has(etsComponentName)) {
1407                extendComponentSymbolMap.get(etsComponentName)!.forEach(local => {
1408                    addPropertySymbol(local, /* insertAwait */ false, insertQuestionDot);
1409                });
1410            }
1411        }
1412
1413        function addEtsStylesPropertySymbol(node: EtsComponentExpression, insertQuestionDot: boolean) {
1414            const locals = getSourceFileOfNode(node).locals;
1415            if (!locals) {
1416                return;
1417            }
1418            const etsComponentName = isIdentifier(node.expression) ? node.expression.escapedText : undefined;
1419            const stylesComponentSymbolMap: UnderscoreEscapedMap<Symbol[]> = new Map<__String, Symbol[]>();
1420            locals.forEach(local => {
1421                if (hasEtsStylesDecoratorNames(local.valueDeclaration?.decorators, compilerOptions)) {
1422                    if (stylesComponentSymbolMap.has(local.escapedName)) {
1423                        stylesComponentSymbolMap.get(local.escapedName)!.push(local);
1424                    }
1425                    else {
1426                        stylesComponentSymbolMap.set(local.escapedName, [local]);
1427                    }
1428                }
1429            });
1430
1431            // If it's a '@Styles' method inside StructDeclaration,
1432            // we will find container StructDeclaration of current node first,
1433            // and then find method decorated with '@Styles'
1434            getContainingStruct(node)?.symbol.members?.forEach(member => {
1435                if (hasEtsStylesDecoratorNames(member.valueDeclaration?.decorators, compilerOptions)) {
1436                    if (stylesComponentSymbolMap.has(member.escapedName)) {
1437                        stylesComponentSymbolMap.get(member.escapedName)!.push(member);
1438                    }
1439                    else {
1440                        stylesComponentSymbolMap.set(member.escapedName, [member]);
1441                    }
1442                }
1443            });
1444
1445            if (etsComponentName && stylesComponentSymbolMap.size > 0) {
1446                stylesComponentSymbolMap.forEach(symbols => {
1447                    symbols.forEach(symbol => {
1448                        addPropertySymbol(symbol, /* insertAwait */ false, insertQuestionDot);
1449                    });
1450                });
1451            }
1452        }
1453
1454        /** Given 'a.b.c', returns 'a'. */
1455        function getLeftMostName(e: Expression): Identifier | undefined {
1456            return isIdentifier(e) ? e : isPropertyAccessExpression(e) ? getLeftMostName(e.expression) : undefined;
1457        }
1458
1459        function tryGetGlobalSymbols(): boolean {
1460            const result: GlobalsSearch = tryGetObjectLikeCompletionSymbols()
1461                || tryGetImportOrExportClauseCompletionSymbols()
1462                || tryGetLocalNamedExportCompletionSymbols()
1463                || tryGetConstructorCompletion()
1464                || tryGetClassLikeCompletionSymbols()
1465                || tryGetJsxCompletionSymbols()
1466                || (getGlobalCompletions(), GlobalsSearch.Success);
1467            return result === GlobalsSearch.Success;
1468        }
1469
1470        function tryGetConstructorCompletion(): GlobalsSearch {
1471            if (!tryGetConstructorLikeCompletionContainer(contextToken)) return GlobalsSearch.Continue;
1472            // no members, only keywords
1473            completionKind = CompletionKind.None;
1474            // Declaring new property/method/accessor
1475            isNewIdentifierLocation = true;
1476            // Has keywords for constructor parameter
1477            keywordFilters = KeywordCompletionFilters.ConstructorParameterKeywords;
1478            return GlobalsSearch.Success;
1479        }
1480
1481        function tryGetJsxCompletionSymbols(): GlobalsSearch {
1482            const jsxContainer = tryGetContainingJsxElement(contextToken);
1483            // Cursor is inside a JSX self-closing element or opening element
1484            const attrsType = jsxContainer && typeChecker.getContextualType(jsxContainer.attributes);
1485            if (!attrsType) return GlobalsSearch.Continue;
1486            const completionsType = jsxContainer && typeChecker.getContextualType(jsxContainer.attributes, ContextFlags.Completions);
1487            symbols = filterJsxAttributes(getPropertiesForObjectExpression(attrsType, completionsType, jsxContainer!.attributes, typeChecker), jsxContainer!.attributes.properties);
1488            setSortTextToOptionalMember();
1489            completionKind = CompletionKind.MemberLike;
1490            isNewIdentifierLocation = false;
1491            return GlobalsSearch.Success;
1492        }
1493
1494        function getGlobalCompletions(): void {
1495            keywordFilters = tryGetFunctionLikeBodyCompletionContainer(contextToken) ? KeywordCompletionFilters.FunctionLikeBodyKeywords : KeywordCompletionFilters.All;
1496
1497            // Get all entities in the current scope.
1498            completionKind = CompletionKind.Global;
1499            isNewIdentifierLocation = isNewIdentifierDefinitionLocation();
1500
1501            if (previousToken !== contextToken) {
1502                Debug.assert(!!previousToken, "Expected 'contextToken' to be defined when different from 'previousToken'.");
1503            }
1504            // We need to find the node that will give us an appropriate scope to begin
1505            // aggregating completion candidates. This is achieved in 'getScopeNode'
1506            // by finding the first node that encompasses a position, accounting for whether a node
1507            // is "complete" to decide whether a position belongs to the node.
1508            //
1509            // However, at the end of an identifier, we are interested in the scope of the identifier
1510            // itself, but fall outside of the identifier. For instance:
1511            //
1512            //      xyz => x$
1513            //
1514            // the cursor is outside of both the 'x' and the arrow function 'xyz => x',
1515            // so 'xyz' is not returned in our results.
1516            //
1517            // We define 'adjustedPosition' so that we may appropriately account for
1518            // being at the end of an identifier. The intention is that if requesting completion
1519            // at the end of an identifier, it should be effectively equivalent to requesting completion
1520            // anywhere inside/at the beginning of the identifier. So in the previous case, the
1521            // 'adjustedPosition' will work as if requesting completion in the following:
1522            //
1523            //      xyz => $x
1524            //
1525            // If previousToken !== contextToken, then
1526            //   - 'contextToken' was adjusted to the token prior to 'previousToken'
1527            //      because we were at the end of an identifier.
1528            //   - 'previousToken' is defined.
1529            const adjustedPosition = previousToken !== contextToken ?
1530                previousToken.getStart() :
1531                position;
1532
1533            const scopeNode = getScopeNode(contextToken, adjustedPosition, sourceFile) || sourceFile;
1534            isInSnippetScope = isSnippetScope(scopeNode);
1535
1536            const symbolMeanings = (isTypeOnly ? SymbolFlags.None : SymbolFlags.Value) | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias;
1537
1538            symbols = typeChecker.getSymbolsInScope(scopeNode, symbolMeanings);
1539            Debug.assertEachIsDefined(symbols, "getSymbolsInScope() should all be defined");
1540            for (const symbol of symbols) {
1541                if (!typeChecker.isArgumentsSymbol(symbol) &&
1542                    !some(symbol.declarations, d => d.getSourceFile() === sourceFile)) {
1543                    symbolToSortTextMap[getSymbolId(symbol)] = SortText.GlobalsOrKeywords;
1544                }
1545            }
1546
1547            // Need to insert 'this.' before properties of `this` type, so only do that if `includeInsertTextCompletions`
1548            if (preferences.includeCompletionsWithInsertText && scopeNode.kind !== SyntaxKind.SourceFile) {
1549                const thisType = typeChecker.tryGetThisTypeAt(scopeNode, /*includeGlobalThis*/ false);
1550                if (thisType && !isProbablyGlobalType(thisType, sourceFile, typeChecker)) {
1551                    for (const symbol of getPropertiesForCompletion(thisType, typeChecker)) {
1552                        symbolToOriginInfoMap[getSymbolId(symbol)] = { kind: SymbolOriginInfoKind.ThisType };
1553                        symbols.push(symbol);
1554                        symbolToSortTextMap[getSymbolId(symbol)] = SortText.SuggestedClassMembers;
1555                    }
1556                }
1557            }
1558
1559            if (shouldOfferImportCompletions()) {
1560                const lowerCaseTokenText = previousToken && isIdentifier(previousToken) ? previousToken.text.toLowerCase() : "";
1561                const autoImportSuggestions = getSymbolsFromOtherSourceFileExports(program.getCompilerOptions().target!, host);
1562                if (!detailsEntryId && importSuggestionsCache) {
1563                    importSuggestionsCache.set(sourceFile.fileName, autoImportSuggestions, host.getProjectVersion && host.getProjectVersion());
1564                }
1565                autoImportSuggestions.forEach(({ symbol, symbolName, skipFilter, origin }) => {
1566                    if (detailsEntryId) {
1567                        if (detailsEntryId.source && stripQuotes(origin.moduleSymbol.name) !== detailsEntryId.source) {
1568                            return;
1569                        }
1570                    }
1571                    else if (!skipFilter && !stringContainsCharactersInOrder(symbolName.toLowerCase(), lowerCaseTokenText)) {
1572                        return;
1573                    }
1574
1575                    const symbolId = getSymbolId(symbol);
1576                    symbols.push(symbol);
1577                    symbolToOriginInfoMap[symbolId] = origin;
1578                    symbolToSortTextMap[symbolId] = SortText.AutoImportSuggestions;
1579                });
1580            }
1581            filterGlobalCompletion(symbols);
1582        }
1583
1584        function shouldOfferImportCompletions(): boolean {
1585            // If current completion is for non-contextual Object literal shortahands, ignore auto-import symbols
1586            if (isNonContextualObjectLiteral) return false;
1587            // If not already a module, must have modules enabled.
1588            if (!preferences.includeCompletionsForModuleExports) return false;
1589            // If already using ES6 modules, OK to continue using them.
1590            if (sourceFile.externalModuleIndicator || sourceFile.commonJsModuleIndicator) return true;
1591            // If module transpilation is enabled or we're targeting es6 or above, or not emitting, OK.
1592            if (compilerOptionsIndicateEs6Modules(program.getCompilerOptions())) return true;
1593            // If some file is using ES6 modules, assume that it's OK to add more.
1594            return programContainsModules(program);
1595        }
1596
1597        function isSnippetScope(scopeNode: Node): boolean {
1598            switch (scopeNode.kind) {
1599                case SyntaxKind.SourceFile:
1600                case SyntaxKind.TemplateExpression:
1601                case SyntaxKind.JsxExpression:
1602                case SyntaxKind.Block:
1603                    return true;
1604                default:
1605                    return isStatement(scopeNode);
1606            }
1607        }
1608
1609        function filterGlobalCompletion(symbols: Symbol[]): void {
1610            const isTypeOnly = isTypeOnlyCompletion();
1611            if (isTypeOnly) {
1612                keywordFilters = contextToken && isAssertionExpression(contextToken.parent)
1613                    ? KeywordCompletionFilters.TypeAssertionKeywords
1614                    : KeywordCompletionFilters.TypeKeywords;
1615            }
1616
1617            const variableDeclaration = getVariableDeclaration(location);
1618
1619            filterMutate(symbols, symbol => {
1620                if (!isSourceFile(location)) {
1621                    // export = /**/ here we want to get all meanings, so any symbol is ok
1622                    if (isExportAssignment(location.parent)) {
1623                        return true;
1624                    }
1625
1626                    // Filter out variables from their own initializers
1627                    // `const a = /* no 'a' here */`
1628                    if (variableDeclaration && symbol.valueDeclaration === variableDeclaration) {
1629                        return false;
1630                    }
1631
1632                    // External modules can have global export declarations that will be
1633                    // available as global keywords in all scopes. But if the external module
1634                    // already has an explicit export and user only wants to user explicit
1635                    // module imports then the global keywords will be filtered out so auto
1636                    // import suggestions will win in the completion
1637                    const symbolOrigin = skipAlias(symbol, typeChecker);
1638                    // We only want to filter out the global keywords
1639                    // Auto Imports are not available for scripts so this conditional is always false
1640                    if (!!sourceFile.externalModuleIndicator
1641                        && !compilerOptions.allowUmdGlobalAccess
1642                        && symbolToSortTextMap[getSymbolId(symbol)] === SortText.GlobalsOrKeywords
1643                        && symbolToSortTextMap[getSymbolId(symbolOrigin)] === SortText.AutoImportSuggestions) {
1644                        return false;
1645                    }
1646                    // Continue with origin symbol
1647                    symbol = symbolOrigin;
1648
1649                    // import m = /**/ <-- It can only access namespace (if typing import = x. this would get member symbols and not namespace)
1650                    if (isInRightSideOfInternalImportEqualsDeclaration(location)) {
1651                        return !!(symbol.flags & SymbolFlags.Namespace);
1652                    }
1653
1654                    if (isTypeOnly) {
1655                        // It's a type, but you can reach it by namespace.type as well
1656                        return symbolCanBeReferencedAtTypeLocation(symbol);
1657                    }
1658                }
1659
1660                // expressions are value space (which includes the value namespaces)
1661                return !!(getCombinedLocalAndExportSymbolFlags(symbol) & SymbolFlags.Value);
1662            });
1663        }
1664
1665        function getVariableDeclaration(property: Node): VariableDeclaration | undefined {
1666            const variableDeclaration = findAncestor(property, node =>
1667                isFunctionBlock(node) || isArrowFunctionBody(node) || isBindingPattern(node)
1668                    ? "quit"
1669                    : isVariableDeclaration(node));
1670
1671            return variableDeclaration as VariableDeclaration | undefined;
1672        }
1673
1674        function isArrowFunctionBody(node: Node) {
1675            return node.parent && isArrowFunction(node.parent) && node.parent.body === node;
1676        };
1677
1678        function isTypeOnlyCompletion(): boolean {
1679            return insideJsDocTagTypeExpression
1680                || !isContextTokenValueLocation(contextToken) &&
1681                (isPossiblyTypeArgumentPosition(contextToken, sourceFile, typeChecker)
1682                    || isPartOfTypeNode(location)
1683                    || isContextTokenTypeLocation(contextToken));
1684        }
1685
1686        function isContextTokenValueLocation(contextToken: Node) {
1687            return contextToken &&
1688                contextToken.kind === SyntaxKind.TypeOfKeyword &&
1689                (contextToken.parent.kind === SyntaxKind.TypeQuery || isTypeOfExpression(contextToken.parent));
1690        }
1691
1692        function isContextTokenTypeLocation(contextToken: Node): boolean {
1693            if (contextToken) {
1694                const parentKind = contextToken.parent.kind;
1695                switch (contextToken.kind) {
1696                    case SyntaxKind.ColonToken:
1697                        return parentKind === SyntaxKind.PropertyDeclaration ||
1698                            parentKind === SyntaxKind.PropertySignature ||
1699                            parentKind === SyntaxKind.Parameter ||
1700                            parentKind === SyntaxKind.VariableDeclaration ||
1701                            isFunctionLikeKind(parentKind);
1702
1703                    case SyntaxKind.EqualsToken:
1704                        return parentKind === SyntaxKind.TypeAliasDeclaration;
1705
1706                    case SyntaxKind.AsKeyword:
1707                        return parentKind === SyntaxKind.AsExpression;
1708
1709                    case SyntaxKind.LessThanToken:
1710                        return parentKind === SyntaxKind.TypeReference ||
1711                            parentKind === SyntaxKind.TypeAssertionExpression;
1712
1713                    case SyntaxKind.ExtendsKeyword:
1714                        return parentKind === SyntaxKind.TypeParameter;
1715                }
1716            }
1717            return false;
1718        }
1719
1720        /** True if symbol is a type or a module containing at least one type. */
1721        function symbolCanBeReferencedAtTypeLocation(symbol: Symbol, seenModules = new Map<string, true>()): boolean {
1722            const sym = skipAlias(symbol.exportSymbol || symbol, typeChecker);
1723            return !!(sym.flags & SymbolFlags.Type) ||
1724                !!(sym.flags & SymbolFlags.Module) &&
1725                addToSeen(seenModules, getSymbolId(sym)) &&
1726                typeChecker.getExportsOfModule(sym).some(e => symbolCanBeReferencedAtTypeLocation(e, seenModules));
1727        }
1728
1729        /**
1730         * Gathers symbols that can be imported from other files, de-duplicating along the way. Symbols can be "duplicates"
1731         * if re-exported from another module, e.g. `export { foo } from "./a"`. That syntax creates a fresh symbol, but
1732         * it’s just an alias to the first, and both have the same name, so we generally want to filter those aliases out,
1733         * if and only if the the first can be imported (it may be excluded due to package.json filtering in
1734         * `codefix.forEachExternalModuleToImportFrom`).
1735         *
1736         * Example. Imagine a chain of node_modules re-exporting one original symbol:
1737         *
1738         * ```js
1739         *  node_modules/x/index.js         node_modules/y/index.js           node_modules/z/index.js
1740         * +-----------------------+      +--------------------------+      +--------------------------+
1741         * |                       |      |                          |      |                          |
1742         * | export const foo = 0; | <--- | export { foo } from 'x'; | <--- | export { foo } from 'y'; |
1743         * |                       |      |                          |      |                          |
1744         * +-----------------------+      +--------------------------+      +--------------------------+
1745         * ```
1746         *
1747         * Also imagine three buckets, which we’ll reference soon:
1748         *
1749         * ```md
1750         * |                  |      |                      |      |                   |
1751         * |   **Bucket A**   |      |    **Bucket B**      |      |    **Bucket C**   |
1752         * |    Symbols to    |      | Aliases to symbols   |      | Symbols to return |
1753         * |    definitely    |      | in Buckets A or C    |      | if nothing better |
1754         * |      return      |      | (don’t return these) |      |    comes along    |
1755         * |__________________|      |______________________|      |___________________|
1756         * ```
1757         *
1758         * We _probably_ want to show `foo` from 'x', but not from 'y' or 'z'. However, if 'x' is not in a package.json, it
1759         * will not appear in a `forEachExternalModuleToImportFrom` iteration. Furthermore, the order of iterations is not
1760         * guaranteed, as it is host-dependent. Therefore, when presented with the symbol `foo` from module 'y' alone, we
1761         * may not be sure whether or not it should go in the list. So, we’ll take the following steps:
1762         *
1763         * 1. Resolve alias `foo` from 'y' to the export declaration in 'x', get the symbol there, and see if that symbol is
1764         *    already in Bucket A (symbols we already know will be returned). If it is, put `foo` from 'y' in Bucket B
1765         *    (symbols that are aliases to symbols in Bucket A). If it’s not, put it in Bucket C.
1766         * 2. Next, imagine we see `foo` from module 'z'. Again, we resolve the alias to the nearest export, which is in 'y'.
1767         *    At this point, if that nearest export from 'y' is in _any_ of the three buckets, we know the symbol in 'z'
1768         *    should never be returned in the final list, so put it in Bucket B.
1769         * 3. Next, imagine we see `foo` from module 'x', the original. Syntactically, it doesn’t look like a re-export, so
1770         *    we can just check Bucket C to see if we put any aliases to the original in there. If they exist, throw them out.
1771         *    Put this symbol in Bucket A.
1772         * 4. After we’ve iterated through every symbol of every module, any symbol left in Bucket C means that step 3 didn’t
1773         *    occur for that symbol---that is, the original symbol is not in Bucket A, so we should include the alias. Move
1774         *    everything from Bucket C to Bucket A.
1775         */
1776        function getSymbolsFromOtherSourceFileExports(target: ScriptTarget, host: LanguageServiceHost): readonly AutoImportSuggestion[] {
1777            const cached = importSuggestionsCache && importSuggestionsCache.get(
1778                sourceFile.fileName,
1779                typeChecker,
1780                detailsEntryId && host.getProjectVersion ? host.getProjectVersion() : undefined);
1781
1782            if (cached) {
1783                log("getSymbolsFromOtherSourceFileExports: Using cached list");
1784                return cached;
1785            }
1786
1787            const startTime = timestamp();
1788            log(`getSymbolsFromOtherSourceFileExports: Recomputing list${detailsEntryId ? " for details entry" : ""}`);
1789            const seenResolvedModules = new Map<string, true>();
1790            const seenExports = new Map<string, true>();
1791            /** Bucket B */
1792            const aliasesToAlreadyIncludedSymbols = new Map<string, true>();
1793            /** Bucket C */
1794            const aliasesToReturnIfOriginalsAreMissing = new Map<string, { alias: Symbol, moduleSymbol: Symbol, isFromPackageJson: boolean }>();
1795            /** Bucket A */
1796            const results: AutoImportSuggestion[] = [];
1797            /** Ids present in `results` for faster lookup */
1798            const resultSymbolIds = new Map<string, true>();
1799
1800            codefix.forEachExternalModuleToImportFrom(program, host, sourceFile, !detailsEntryId, /*useAutoImportProvider*/ true, (moduleSymbol, _, program, isFromPackageJson) => {
1801                // Perf -- ignore other modules if this is a request for details
1802                if (detailsEntryId && detailsEntryId.source && stripQuotes(moduleSymbol.name) !== detailsEntryId.source) {
1803                    return;
1804                }
1805
1806                const typeChecker = program.getTypeChecker();
1807                const resolvedModuleSymbol = typeChecker.resolveExternalModuleSymbol(moduleSymbol);
1808                // resolvedModuleSymbol may be a namespace. A namespace may be `export =` by multiple module declarations, but only keep the first one.
1809                if (!addToSeen(seenResolvedModules, getSymbolId(resolvedModuleSymbol))) {
1810                    return;
1811                }
1812
1813                // Don't add another completion for `export =` of a symbol that's already global.
1814                // So in `declare namespace foo {} declare module "foo" { export = foo; }`, there will just be the global completion for `foo`.
1815                if (resolvedModuleSymbol !== moduleSymbol && every(resolvedModuleSymbol.declarations, isNonGlobalDeclaration)) {
1816                    pushSymbol(resolvedModuleSymbol, moduleSymbol, isFromPackageJson, /*skipFilter*/ true);
1817                }
1818
1819                for (const symbol of typeChecker.getExportsAndPropertiesOfModule(moduleSymbol)) {
1820                    const symbolId = getSymbolId(symbol).toString();
1821                    // `getExportsAndPropertiesOfModule` can include duplicates
1822                    if (!addToSeen(seenExports, symbolId)) {
1823                        continue;
1824                    }
1825                    // If this is `export { _break as break };` (a keyword) -- skip this and prefer the keyword completion.
1826                    if (some(symbol.declarations, d => isExportSpecifier(d) && !!d.propertyName && isIdentifierANonContextualKeyword(d.name))) {
1827                        continue;
1828                    }
1829
1830                    // If `symbol.parent !== moduleSymbol`, this is an `export * from "foo"` re-export. Those don't create new symbols.
1831                    const isExportStarFromReExport = typeChecker.getMergedSymbol(symbol.parent!) !== resolvedModuleSymbol;
1832                    // If `!!d.parent.parent.moduleSpecifier`, this is `export { foo } from "foo"` re-export, which creates a new symbol (thus isn't caught by the first check).
1833                    if (isExportStarFromReExport || some(symbol.declarations, d => isExportSpecifier(d) && !d.propertyName && !!d.parent.parent.moduleSpecifier)) {
1834                        // Walk the export chain back one module (step 1 or 2 in diagrammed example).
1835                        // Or, in the case of `export * from "foo"`, `symbol` already points to the original export, so just use that.
1836                        const nearestExportSymbol = isExportStarFromReExport ? symbol : getNearestExportSymbol(symbol);
1837                        if (!nearestExportSymbol) continue;
1838                        const nearestExportSymbolId = getSymbolId(nearestExportSymbol).toString();
1839                        const symbolHasBeenSeen = resultSymbolIds.has(nearestExportSymbolId) || aliasesToAlreadyIncludedSymbols.has(nearestExportSymbolId);
1840                        if (!symbolHasBeenSeen) {
1841                            aliasesToReturnIfOriginalsAreMissing.set(nearestExportSymbolId, { alias: symbol, moduleSymbol, isFromPackageJson });
1842                            aliasesToAlreadyIncludedSymbols.set(symbolId, true);
1843                        }
1844                        else {
1845                            // Perf - we know this symbol is an alias to one that’s already covered in `symbols`, so store it here
1846                            // in case another symbol re-exports this one; that way we can short-circuit as soon as we see this symbol id.
1847                            addToSeen(aliasesToAlreadyIncludedSymbols, symbolId);
1848                        }
1849                    }
1850                    else {
1851                        // This is not a re-export, so see if we have any aliases pending and remove them (step 3 in diagrammed example)
1852                        aliasesToReturnIfOriginalsAreMissing.delete(symbolId);
1853                        pushSymbol(symbol, moduleSymbol, isFromPackageJson, /*skipFilter*/ false);
1854                    }
1855                }
1856            });
1857
1858            // By this point, any potential duplicates that were actually duplicates have been
1859            // removed, so the rest need to be added. (Step 4 in diagrammed example)
1860            aliasesToReturnIfOriginalsAreMissing.forEach(({ alias, moduleSymbol, isFromPackageJson }) => pushSymbol(alias, moduleSymbol, isFromPackageJson, /*skipFilter*/ false));
1861            log(`getSymbolsFromOtherSourceFileExports: ${timestamp() - startTime}`);
1862            return results;
1863
1864            function pushSymbol(symbol: Symbol, moduleSymbol: Symbol, isFromPackageJson: boolean, skipFilter: boolean) {
1865                const isDefaultExport = symbol.escapedName === InternalSymbolName.Default;
1866                if (isDefaultExport) {
1867                    symbol = getLocalSymbolForExportDefault(symbol) || symbol;
1868                }
1869                if (typeChecker.isUndefinedSymbol(symbol)) {
1870                    return;
1871                }
1872                addToSeen(resultSymbolIds, getSymbolId(symbol));
1873                const origin: SymbolOriginInfoExport = { kind: SymbolOriginInfoKind.Export, moduleSymbol, isDefaultExport, isFromPackageJson };
1874                results.push({
1875                    symbol,
1876                    symbolName: getNameForExportedSymbol(symbol, target),
1877                    origin,
1878                    skipFilter,
1879                });
1880            }
1881        }
1882
1883        function getNearestExportSymbol(fromSymbol: Symbol) {
1884            return findAlias(typeChecker, fromSymbol, alias => {
1885                return some(alias.declarations, d => isExportSpecifier(d) || !!d.localSymbol);
1886            });
1887        }
1888
1889        /**
1890         * True if you could remove some characters in `a` to get `b`.
1891         * E.g., true for "abcdef" and "bdf".
1892         * But not true for "abcdef" and "dbf".
1893         */
1894        function stringContainsCharactersInOrder(str: string, characters: string): boolean {
1895            if (characters.length === 0) {
1896                return true;
1897            }
1898
1899            let characterIndex = 0;
1900            for (let strIndex = 0; strIndex < str.length; strIndex++) {
1901                if (str.charCodeAt(strIndex) === characters.charCodeAt(characterIndex)) {
1902                    characterIndex++;
1903                    if (characterIndex === characters.length) {
1904                        return true;
1905                    }
1906                }
1907            }
1908
1909            // Did not find all characters
1910            return false;
1911        }
1912
1913        /**
1914         * Finds the first node that "embraces" the position, so that one may
1915         * accurately aggregate locals from the closest containing scope.
1916         */
1917        function getScopeNode(initialToken: Node | undefined, position: number, sourceFile: SourceFile) {
1918            let scope: Node | undefined = initialToken;
1919            while (scope && !positionBelongsToNode(scope, position, sourceFile)) {
1920                scope = scope.parent;
1921            }
1922            return scope;
1923        }
1924
1925        function isCompletionListBlocker(contextToken: Node): boolean {
1926            const start = timestamp();
1927            const result = isInStringOrRegularExpressionOrTemplateLiteral(contextToken) ||
1928                isSolelyIdentifierDefinitionLocation(contextToken) ||
1929                isDotOfNumericLiteral(contextToken) ||
1930                isInJsxText(contextToken);
1931            log("getCompletionsAtPosition: isCompletionListBlocker: " + (timestamp() - start));
1932            return result;
1933        }
1934
1935        function isInJsxText(contextToken: Node): boolean {
1936            if (contextToken.kind === SyntaxKind.JsxText) {
1937                return true;
1938            }
1939
1940            if (contextToken.kind === SyntaxKind.GreaterThanToken && contextToken.parent) {
1941                if (contextToken.parent.kind === SyntaxKind.JsxOpeningElement) {
1942                    // Two possibilities:
1943                    //   1. <div>/**/
1944                    //      - contextToken: GreaterThanToken (before cursor)
1945                    //      - location: JSXElement
1946                    //      - different parents (JSXOpeningElement, JSXElement)
1947                    //   2. <Component<string> /**/>
1948                    //      - contextToken: GreaterThanToken (before cursor)
1949                    //      - location: GreaterThanToken (after cursor)
1950                    //      - same parent (JSXOpeningElement)
1951                    return location.parent.kind !== SyntaxKind.JsxOpeningElement;
1952                }
1953
1954                if (contextToken.parent.kind === SyntaxKind.JsxClosingElement || contextToken.parent.kind === SyntaxKind.JsxSelfClosingElement) {
1955                    return !!contextToken.parent.parent && contextToken.parent.parent.kind === SyntaxKind.JsxElement;
1956                }
1957            }
1958            return false;
1959        }
1960
1961        function isNewIdentifierDefinitionLocation(): boolean {
1962            if (contextToken) {
1963                const containingNodeKind = contextToken.parent.kind;
1964                // Previous token may have been a keyword that was converted to an identifier.
1965                switch (keywordForNode(contextToken)) {
1966                    case SyntaxKind.CommaToken:
1967                        return containingNodeKind === SyntaxKind.CallExpression               // func( a, |
1968                            || containingNodeKind === SyntaxKind.Constructor                  // constructor( a, |   /* public, protected, private keywords are allowed here, so show completion */
1969                            || containingNodeKind === SyntaxKind.NewExpression                // new C(a, |
1970                            || containingNodeKind === SyntaxKind.ArrayLiteralExpression       // [a, |
1971                            || containingNodeKind === SyntaxKind.BinaryExpression             // const x = (a, |
1972                            || containingNodeKind === SyntaxKind.FunctionType                 // var x: (s: string, list|
1973                            || containingNodeKind === SyntaxKind.ObjectLiteralExpression;     // const obj = { x, |
1974
1975                    case SyntaxKind.OpenParenToken:
1976                        return containingNodeKind === SyntaxKind.CallExpression               // func( |
1977                            || containingNodeKind === SyntaxKind.Constructor                  // constructor( |
1978                            || containingNodeKind === SyntaxKind.NewExpression                // new C(a|
1979                            || containingNodeKind === SyntaxKind.ParenthesizedExpression      // const x = (a|
1980                            || containingNodeKind === SyntaxKind.ParenthesizedType;           // function F(pred: (a| /* this can become an arrow function, where 'a' is the argument */
1981
1982                    case SyntaxKind.OpenBracketToken:
1983                        return containingNodeKind === SyntaxKind.ArrayLiteralExpression       // [ |
1984                            || containingNodeKind === SyntaxKind.IndexSignature               // [ | : string ]
1985                            || containingNodeKind === SyntaxKind.ComputedPropertyName;         // [ |    /* this can become an index signature */
1986
1987                    case SyntaxKind.ModuleKeyword:                                            // module |
1988                    case SyntaxKind.NamespaceKeyword:                                         // namespace |
1989                        return true;
1990
1991                    case SyntaxKind.DotToken:
1992                        return containingNodeKind === SyntaxKind.ModuleDeclaration;           // module A.|
1993
1994                    case SyntaxKind.OpenBraceToken:
1995                        return containingNodeKind === SyntaxKind.ClassDeclaration             // class A { |
1996                            || containingNodeKind === SyntaxKind.StructDeclaration            // struct A { |
1997                            || containingNodeKind === SyntaxKind.ObjectLiteralExpression;     // const obj = { |
1998
1999                    case SyntaxKind.EqualsToken:
2000                        return containingNodeKind === SyntaxKind.VariableDeclaration          // const x = a|
2001                            || containingNodeKind === SyntaxKind.BinaryExpression;            // x = a|
2002
2003                    case SyntaxKind.TemplateHead:
2004                        return containingNodeKind === SyntaxKind.TemplateExpression;          // `aa ${|
2005
2006                    case SyntaxKind.TemplateMiddle:
2007                        return containingNodeKind === SyntaxKind.TemplateSpan;                // `aa ${10} dd ${|
2008
2009                    case SyntaxKind.PublicKeyword:
2010                    case SyntaxKind.PrivateKeyword:
2011                    case SyntaxKind.ProtectedKeyword:
2012                        return containingNodeKind === SyntaxKind.PropertyDeclaration;         // class A{ public |
2013                }
2014            }
2015
2016            return false;
2017        }
2018
2019        function isInStringOrRegularExpressionOrTemplateLiteral(contextToken: Node): boolean {
2020            // To be "in" one of these literals, the position has to be:
2021            //   1. entirely within the token text.
2022            //   2. at the end position of an unterminated token.
2023            //   3. at the end of a regular expression (due to trailing flags like '/foo/g').
2024            return (isRegularExpressionLiteral(contextToken) || isStringTextContainingNode(contextToken)) && (
2025                rangeContainsPositionExclusive(createTextRangeFromSpan(createTextSpanFromNode(contextToken)), position) ||
2026                position === contextToken.end && (!!contextToken.isUnterminated || isRegularExpressionLiteral(contextToken)));
2027        }
2028
2029        /**
2030         * Aggregates relevant symbols for completion in object literals and object binding patterns.
2031         * Relevant symbols are stored in the captured 'symbols' variable.
2032         *
2033         * @returns true if 'symbols' was successfully populated; false otherwise.
2034         */
2035        function tryGetObjectLikeCompletionSymbols(): GlobalsSearch | undefined {
2036            const objectLikeContainer = tryGetObjectLikeCompletionContainer(contextToken);
2037            if (!objectLikeContainer) return GlobalsSearch.Continue;
2038
2039            // We're looking up possible property names from contextual/inferred/declared type.
2040            completionKind = CompletionKind.ObjectPropertyDeclaration;
2041
2042            let typeMembers: Symbol[] | undefined;
2043            let existingMembers: readonly Declaration[] | undefined;
2044
2045            if (objectLikeContainer.kind === SyntaxKind.ObjectLiteralExpression) {
2046                const instantiatedType = tryGetObjectLiteralContextualType(objectLikeContainer, typeChecker);
2047
2048                // Check completions for Object property value shorthand
2049                if (instantiatedType === undefined) {
2050                    if (objectLikeContainer.flags & NodeFlags.InWithStatement) {
2051                        return GlobalsSearch.Fail;
2052                    }
2053                    isNonContextualObjectLiteral = true;
2054                    return GlobalsSearch.Continue;
2055                }
2056                const completionsType = typeChecker.getContextualType(objectLikeContainer, ContextFlags.Completions);
2057                const hasStringIndexType = (completionsType || instantiatedType).getStringIndexType();
2058                const hasNumberIndextype = (completionsType || instantiatedType).getNumberIndexType();
2059                isNewIdentifierLocation = !!hasStringIndexType || !!hasNumberIndextype;
2060                typeMembers = getPropertiesForObjectExpression(instantiatedType, completionsType, objectLikeContainer, typeChecker);
2061                existingMembers = objectLikeContainer.properties;
2062
2063                if (typeMembers.length === 0) {
2064                    // Edge case: If NumberIndexType exists
2065                    if (!hasNumberIndextype) {
2066                        isNonContextualObjectLiteral = true;
2067                        return GlobalsSearch.Continue;
2068                    }
2069                }
2070            }
2071            else {
2072                Debug.assert(objectLikeContainer.kind === SyntaxKind.ObjectBindingPattern);
2073                // We are *only* completing on properties from the type being destructured.
2074                isNewIdentifierLocation = false;
2075
2076                const rootDeclaration = getRootDeclaration(objectLikeContainer.parent);
2077                if (!isVariableLike(rootDeclaration)) return Debug.fail("Root declaration is not variable-like.");
2078
2079                // We don't want to complete using the type acquired by the shape
2080                // of the binding pattern; we are only interested in types acquired
2081                // through type declaration or inference.
2082                // Also proceed if rootDeclaration is a parameter and if its containing function expression/arrow function is contextually typed -
2083                // type of parameter will flow in from the contextual type of the function
2084                let canGetType = hasInitializer(rootDeclaration) || hasType(rootDeclaration) || rootDeclaration.parent.parent.kind === SyntaxKind.ForOfStatement;
2085                if (!canGetType && rootDeclaration.kind === SyntaxKind.Parameter) {
2086                    if (isExpression(rootDeclaration.parent)) {
2087                        canGetType = !!typeChecker.getContextualType(<Expression>rootDeclaration.parent);
2088                    }
2089                    else if (rootDeclaration.parent.kind === SyntaxKind.MethodDeclaration || rootDeclaration.parent.kind === SyntaxKind.SetAccessor) {
2090                        canGetType = isExpression(rootDeclaration.parent.parent) && !!typeChecker.getContextualType(<Expression>rootDeclaration.parent.parent);
2091                    }
2092                }
2093                if (canGetType) {
2094                    const typeForObject = typeChecker.getTypeAtLocation(objectLikeContainer);
2095                    if (!typeForObject) return GlobalsSearch.Fail;
2096                    // In a binding pattern, get only known properties (unless in the same scope).
2097                    // Everywhere else we will get all possible properties.
2098                    const containerClass = getContainingClass(objectLikeContainer);
2099                    typeMembers = typeChecker.getPropertiesOfType(typeForObject).filter(symbol =>
2100                        // either public
2101                        !(getDeclarationModifierFlagsFromSymbol(symbol) & ModifierFlags.NonPublicAccessibilityModifier)
2102                        // or we're in it
2103                        || containerClass && contains(typeForObject.symbol.declarations, containerClass));
2104                    existingMembers = objectLikeContainer.elements;
2105                }
2106            }
2107
2108            if (typeMembers && typeMembers.length > 0) {
2109                // Add filtered items to the completion list
2110                symbols = filterObjectMembersList(typeMembers, Debug.checkDefined(existingMembers));
2111            }
2112            setSortTextToOptionalMember();
2113
2114            return GlobalsSearch.Success;
2115        }
2116
2117        /**
2118         * Aggregates relevant symbols for completion in import clauses and export clauses
2119         * whose declarations have a module specifier; for instance, symbols will be aggregated for
2120         *
2121         *      import { | } from "moduleName";
2122         *      export { a as foo, | } from "moduleName";
2123         *
2124         * but not for
2125         *
2126         *      export { | };
2127         *
2128         * Relevant symbols are stored in the captured 'symbols' variable.
2129         */
2130        function tryGetImportOrExportClauseCompletionSymbols(): GlobalsSearch {
2131            // `import { |` or `import { a as 0, | }`
2132            const namedImportsOrExports = contextToken && (contextToken.kind === SyntaxKind.OpenBraceToken || contextToken.kind === SyntaxKind.CommaToken)
2133                ? tryCast(contextToken.parent, isNamedImportsOrExports) : undefined;
2134            if (!namedImportsOrExports) return GlobalsSearch.Continue;
2135
2136            // try to show exported member for imported/re-exported module
2137            const { moduleSpecifier } = namedImportsOrExports.kind === SyntaxKind.NamedImports ? namedImportsOrExports.parent.parent : namedImportsOrExports.parent;
2138            if (!moduleSpecifier) return namedImportsOrExports.kind === SyntaxKind.NamedImports ? GlobalsSearch.Fail : GlobalsSearch.Continue;
2139            const moduleSpecifierSymbol = typeChecker.getSymbolAtLocation(moduleSpecifier); // TODO: GH#18217
2140            if (!moduleSpecifierSymbol) return GlobalsSearch.Fail;
2141
2142            completionKind = CompletionKind.MemberLike;
2143            isNewIdentifierLocation = false;
2144            const exports = typeChecker.getExportsAndPropertiesOfModule(moduleSpecifierSymbol);
2145            const existing = new Set((namedImportsOrExports.elements as NodeArray<ImportOrExportSpecifier>).filter(n => !isCurrentlyEditingNode(n)).map(n => (n.propertyName || n.name).escapedText));
2146            symbols = exports.filter(e => e.escapedName !== InternalSymbolName.Default && !existing.has(e.escapedName));
2147            return GlobalsSearch.Success;
2148        }
2149
2150        /**
2151         * Adds local declarations for completions in named exports:
2152         *
2153         *   export { | };
2154         *
2155         * Does not check for the absence of a module specifier (`export {} from "./other"`)
2156         * because `tryGetImportOrExportClauseCompletionSymbols` runs first and handles that,
2157         * preventing this function from running.
2158         */
2159        function tryGetLocalNamedExportCompletionSymbols(): GlobalsSearch {
2160            const namedExports = contextToken && (contextToken.kind === SyntaxKind.OpenBraceToken || contextToken.kind === SyntaxKind.CommaToken)
2161                ? tryCast(contextToken.parent, isNamedExports)
2162                : undefined;
2163
2164            if (!namedExports) {
2165                return GlobalsSearch.Continue;
2166            }
2167
2168            const localsContainer = findAncestor(namedExports, or(isSourceFile, isModuleDeclaration))!;
2169            completionKind = CompletionKind.None;
2170            isNewIdentifierLocation = false;
2171            localsContainer.locals?.forEach((symbol, name) => {
2172                symbols.push(symbol);
2173                if (localsContainer.symbol?.exports?.has(name)) {
2174                    symbolToSortTextMap[getSymbolId(symbol)] = SortText.OptionalMember;
2175                }
2176            });
2177            return GlobalsSearch.Success;
2178        }
2179
2180        /**
2181         * Aggregates relevant symbols for completion in class declaration
2182         * Relevant symbols are stored in the captured 'symbols' variable.
2183         */
2184        function tryGetClassLikeCompletionSymbols(): GlobalsSearch {
2185            const decl = tryGetObjectTypeDeclarationCompletionContainer(sourceFile, contextToken, location, position);
2186            if (!decl) return GlobalsSearch.Continue;
2187
2188            // We're looking up possible property names from parent type.
2189            completionKind = CompletionKind.MemberLike;
2190            // Declaring new property/method/accessor
2191            isNewIdentifierLocation = true;
2192            keywordFilters = contextToken.kind === SyntaxKind.AsteriskToken ? KeywordCompletionFilters.None :
2193                isClassLike(decl) ? KeywordCompletionFilters.ClassElementKeywords : KeywordCompletionFilters.InterfaceElementKeywords;
2194
2195            // If you're in an interface you don't want to repeat things from super-interface. So just stop here.
2196            if (!isClassLike(decl)) return GlobalsSearch.Success;
2197
2198            const classElement = contextToken.kind === SyntaxKind.SemicolonToken ? contextToken.parent.parent : contextToken.parent;
2199            let classElementModifierFlags = isClassElement(classElement) ? getEffectiveModifierFlags(classElement) : ModifierFlags.None;
2200            // If this is context token is not something we are editing now, consider if this would lead to be modifier
2201            if (contextToken.kind === SyntaxKind.Identifier && !isCurrentlyEditingNode(contextToken)) {
2202                switch (contextToken.getText()) {
2203                    case "private":
2204                        classElementModifierFlags = classElementModifierFlags | ModifierFlags.Private;
2205                        break;
2206                    case "static":
2207                        classElementModifierFlags = classElementModifierFlags | ModifierFlags.Static;
2208                        break;
2209                }
2210            }
2211
2212            // No member list for private methods
2213            if (!(classElementModifierFlags & ModifierFlags.Private)) {
2214                // List of property symbols of base type that are not private and already implemented
2215                const baseSymbols = flatMap(getAllSuperTypeNodes(decl), baseTypeNode => {
2216                    const type = typeChecker.getTypeAtLocation(baseTypeNode);
2217                    return classElementModifierFlags & ModifierFlags.Static ?
2218                        type?.symbol && typeChecker.getPropertiesOfType(typeChecker.getTypeOfSymbolAtLocation(type.symbol, decl)) :
2219                        type && typeChecker.getPropertiesOfType(type);
2220                });
2221                symbols = filterClassMembersList(baseSymbols, decl.members, classElementModifierFlags);
2222            }
2223
2224            return GlobalsSearch.Success;
2225        }
2226
2227        /**
2228         * Returns the immediate owning object literal or binding pattern of a context token,
2229         * on the condition that one exists and that the context implies completion should be given.
2230         */
2231        function tryGetObjectLikeCompletionContainer(contextToken: Node): ObjectLiteralExpression | ObjectBindingPattern | undefined {
2232            if (contextToken) {
2233                const { parent } = contextToken;
2234                switch (contextToken.kind) {
2235                    case SyntaxKind.OpenBraceToken:  // const x = { |
2236                    case SyntaxKind.CommaToken:      // const x = { a: 0, |
2237                        if (isObjectLiteralExpression(parent) || isObjectBindingPattern(parent)) {
2238                            return parent;
2239                        }
2240                        break;
2241                    case SyntaxKind.AsteriskToken:
2242                        return isMethodDeclaration(parent) ? tryCast(parent.parent, isObjectLiteralExpression) : undefined;
2243                    case SyntaxKind.Identifier:
2244                        return (contextToken as Identifier).text === "async" && isShorthandPropertyAssignment(contextToken.parent)
2245                            ? contextToken.parent.parent : undefined;
2246                }
2247            }
2248
2249            return undefined;
2250        }
2251
2252        function isConstructorParameterCompletion(node: Node): boolean {
2253            return !!node.parent && isParameter(node.parent) && isConstructorDeclaration(node.parent.parent)
2254                && (isParameterPropertyModifier(node.kind) || isDeclarationName(node));
2255        }
2256
2257        /**
2258         * Returns the immediate owning class declaration of a context token,
2259         * on the condition that one exists and that the context implies completion should be given.
2260         */
2261        function tryGetConstructorLikeCompletionContainer(contextToken: Node): ConstructorDeclaration | undefined {
2262            if (contextToken) {
2263                const parent = contextToken.parent;
2264                switch (contextToken.kind) {
2265                    case SyntaxKind.OpenParenToken:
2266                    case SyntaxKind.CommaToken:
2267                        return isConstructorDeclaration(contextToken.parent) ? contextToken.parent : undefined;
2268
2269                    default:
2270                        if (isConstructorParameterCompletion(contextToken)) {
2271                            return parent.parent as ConstructorDeclaration;
2272                        }
2273                }
2274            }
2275            return undefined;
2276        }
2277
2278        function tryGetFunctionLikeBodyCompletionContainer(contextToken: Node): FunctionLikeDeclaration | undefined {
2279            if (contextToken) {
2280                let prev: Node;
2281                const container = findAncestor(contextToken.parent, (node: Node) => {
2282                    if (isClassLike(node)) {
2283                        return "quit";
2284                    }
2285                    if (isFunctionLikeDeclaration(node) && prev === node.body) {
2286                        return true;
2287                    }
2288                    prev = node;
2289                    return false;
2290                });
2291                return container && container as FunctionLikeDeclaration;
2292            }
2293        }
2294
2295        function tryGetContainingJsxElement(contextToken: Node): JsxOpeningLikeElement | undefined {
2296            if (contextToken) {
2297                const parent = contextToken.parent;
2298                switch (contextToken.kind) {
2299                    case SyntaxKind.GreaterThanToken: // End of a type argument list
2300                    case SyntaxKind.LessThanSlashToken:
2301                    case SyntaxKind.SlashToken:
2302                    case SyntaxKind.Identifier:
2303                    case SyntaxKind.PropertyAccessExpression:
2304                    case SyntaxKind.JsxAttributes:
2305                    case SyntaxKind.JsxAttribute:
2306                    case SyntaxKind.JsxSpreadAttribute:
2307                        if (parent && (parent.kind === SyntaxKind.JsxSelfClosingElement || parent.kind === SyntaxKind.JsxOpeningElement)) {
2308                            if (contextToken.kind === SyntaxKind.GreaterThanToken) {
2309                                const precedingToken = findPrecedingToken(contextToken.pos, sourceFile, /*startNode*/ undefined);
2310                                if (!(parent as JsxOpeningLikeElement).typeArguments || (precedingToken && precedingToken.kind === SyntaxKind.SlashToken)) break;
2311                            }
2312                            return <JsxOpeningLikeElement>parent;
2313                        }
2314                        else if (parent.kind === SyntaxKind.JsxAttribute) {
2315                            // Currently we parse JsxOpeningLikeElement as:
2316                            //      JsxOpeningLikeElement
2317                            //          attributes: JsxAttributes
2318                            //             properties: NodeArray<JsxAttributeLike>
2319                            return parent.parent.parent as JsxOpeningLikeElement;
2320                        }
2321                        break;
2322
2323                    // The context token is the closing } or " of an attribute, which means
2324                    // its parent is a JsxExpression, whose parent is a JsxAttribute,
2325                    // whose parent is a JsxOpeningLikeElement
2326                    case SyntaxKind.StringLiteral:
2327                        if (parent && ((parent.kind === SyntaxKind.JsxAttribute) || (parent.kind === SyntaxKind.JsxSpreadAttribute))) {
2328                            // Currently we parse JsxOpeningLikeElement as:
2329                            //      JsxOpeningLikeElement
2330                            //          attributes: JsxAttributes
2331                            //             properties: NodeArray<JsxAttributeLike>
2332                            return parent.parent.parent as JsxOpeningLikeElement;
2333                        }
2334
2335                        break;
2336
2337                    case SyntaxKind.CloseBraceToken:
2338                        if (parent &&
2339                            parent.kind === SyntaxKind.JsxExpression &&
2340                            parent.parent && parent.parent.kind === SyntaxKind.JsxAttribute) {
2341                            // Currently we parse JsxOpeningLikeElement as:
2342                            //      JsxOpeningLikeElement
2343                            //          attributes: JsxAttributes
2344                            //             properties: NodeArray<JsxAttributeLike>
2345                            //                  each JsxAttribute can have initializer as JsxExpression
2346                            return parent.parent.parent.parent as JsxOpeningLikeElement;
2347                        }
2348
2349                        if (parent && parent.kind === SyntaxKind.JsxSpreadAttribute) {
2350                            // Currently we parse JsxOpeningLikeElement as:
2351                            //      JsxOpeningLikeElement
2352                            //          attributes: JsxAttributes
2353                            //             properties: NodeArray<JsxAttributeLike>
2354                            return parent.parent.parent as JsxOpeningLikeElement;
2355                        }
2356
2357                        break;
2358                }
2359            }
2360            return undefined;
2361        }
2362
2363        /**
2364         * @returns true if we are certain that the currently edited location must define a new location; false otherwise.
2365         */
2366        function isSolelyIdentifierDefinitionLocation(contextToken: Node): boolean {
2367            const parent = contextToken.parent;
2368            const containingNodeKind = parent.kind;
2369            switch (contextToken.kind) {
2370                case SyntaxKind.CommaToken:
2371                    return containingNodeKind === SyntaxKind.VariableDeclaration ||
2372                        isVariableDeclarationListButNotTypeArgument(contextToken) ||
2373                        containingNodeKind === SyntaxKind.VariableStatement ||
2374                        containingNodeKind === SyntaxKind.EnumDeclaration ||                        // enum a { foo, |
2375                        isFunctionLikeButNotConstructor(containingNodeKind) ||
2376                        containingNodeKind === SyntaxKind.InterfaceDeclaration ||                   // interface A<T, |
2377                        containingNodeKind === SyntaxKind.ArrayBindingPattern ||                    // var [x, y|
2378                        containingNodeKind === SyntaxKind.TypeAliasDeclaration ||                   // type Map, K, |
2379                        // class A<T, |
2380                        // var C = class D<T, |
2381                        (isClassLike(parent) &&
2382                            !!parent.typeParameters &&
2383                            parent.typeParameters.end >= contextToken.pos);
2384
2385                case SyntaxKind.DotToken:
2386                    return containingNodeKind === SyntaxKind.ArrayBindingPattern;                   // var [.|
2387
2388                case SyntaxKind.ColonToken:
2389                    return containingNodeKind === SyntaxKind.BindingElement;                        // var {x :html|
2390
2391                case SyntaxKind.OpenBracketToken:
2392                    return containingNodeKind === SyntaxKind.ArrayBindingPattern;                   // var [x|
2393
2394                case SyntaxKind.OpenParenToken:
2395                    return containingNodeKind === SyntaxKind.CatchClause ||
2396                        isFunctionLikeButNotConstructor(containingNodeKind);
2397
2398                case SyntaxKind.OpenBraceToken:
2399                    return containingNodeKind === SyntaxKind.EnumDeclaration;                       // enum a { |
2400
2401                case SyntaxKind.LessThanToken:
2402                    return containingNodeKind === SyntaxKind.ClassDeclaration ||                    // class A< |
2403                        containingNodeKind === SyntaxKind.ClassExpression ||                        // var C = class D< |
2404                        containingNodeKind === SyntaxKind.InterfaceDeclaration ||                   // interface A< |
2405                        containingNodeKind === SyntaxKind.TypeAliasDeclaration ||                   // type List< |
2406                        isFunctionLikeKind(containingNodeKind);
2407
2408                case SyntaxKind.StaticKeyword:
2409                    return containingNodeKind === SyntaxKind.PropertyDeclaration && !isClassLike(parent.parent);
2410
2411                case SyntaxKind.DotDotDotToken:
2412                    return containingNodeKind === SyntaxKind.Parameter ||
2413                        (!!parent.parent && parent.parent.kind === SyntaxKind.ArrayBindingPattern);  // var [...z|
2414
2415                case SyntaxKind.PublicKeyword:
2416                case SyntaxKind.PrivateKeyword:
2417                case SyntaxKind.ProtectedKeyword:
2418                    return containingNodeKind === SyntaxKind.Parameter && !isConstructorDeclaration(parent.parent);
2419
2420                case SyntaxKind.AsKeyword:
2421                    return containingNodeKind === SyntaxKind.ImportSpecifier ||
2422                        containingNodeKind === SyntaxKind.ExportSpecifier ||
2423                        containingNodeKind === SyntaxKind.NamespaceImport;
2424
2425                case SyntaxKind.GetKeyword:
2426                case SyntaxKind.SetKeyword:
2427                    return !isFromObjectTypeDeclaration(contextToken);
2428
2429                case SyntaxKind.ClassKeyword:
2430                case SyntaxKind.StructKeyword:
2431                case SyntaxKind.EnumKeyword:
2432                case SyntaxKind.InterfaceKeyword:
2433                case SyntaxKind.FunctionKeyword:
2434                case SyntaxKind.VarKeyword:
2435                case SyntaxKind.ImportKeyword:
2436                case SyntaxKind.LetKeyword:
2437                case SyntaxKind.ConstKeyword:
2438                case SyntaxKind.InferKeyword:
2439                case SyntaxKind.TypeKeyword:  // type htm|
2440                    return true;
2441
2442                case SyntaxKind.AsteriskToken:
2443                    return isFunctionLike(contextToken.parent) && !isMethodDeclaration(contextToken.parent);
2444            }
2445
2446            // If the previous token is keyword correspoding to class member completion keyword
2447            // there will be completion available here
2448            if (isClassMemberCompletionKeyword(keywordForNode(contextToken)) && isFromObjectTypeDeclaration(contextToken)) {
2449                return false;
2450            }
2451
2452            if (isConstructorParameterCompletion(contextToken)) {
2453                // constructor parameter completion is available only if
2454                // - its modifier of the constructor parameter or
2455                // - its name of the parameter and not being edited
2456                // eg. constructor(a |<- this shouldnt show completion
2457                if (!isIdentifier(contextToken) ||
2458                    isParameterPropertyModifier(keywordForNode(contextToken)) ||
2459                    isCurrentlyEditingNode(contextToken)) {
2460                    return false;
2461                }
2462            }
2463
2464            // Previous token may have been a keyword that was converted to an identifier.
2465            switch (keywordForNode(contextToken)) {
2466                case SyntaxKind.AbstractKeyword:
2467                case SyntaxKind.ClassKeyword:
2468                case SyntaxKind.StructKeyword:
2469                case SyntaxKind.ConstKeyword:
2470                case SyntaxKind.DeclareKeyword:
2471                case SyntaxKind.EnumKeyword:
2472                case SyntaxKind.FunctionKeyword:
2473                case SyntaxKind.InterfaceKeyword:
2474                case SyntaxKind.LetKeyword:
2475                case SyntaxKind.PrivateKeyword:
2476                case SyntaxKind.ProtectedKeyword:
2477                case SyntaxKind.PublicKeyword:
2478                case SyntaxKind.StaticKeyword:
2479                case SyntaxKind.VarKeyword:
2480                    return true;
2481                case SyntaxKind.AsyncKeyword:
2482                    return isPropertyDeclaration(contextToken.parent);
2483            }
2484
2485            return isDeclarationName(contextToken)
2486                && !isShorthandPropertyAssignment(contextToken.parent)
2487                && !isJsxAttribute(contextToken.parent)
2488                // Don't block completions if we're in `class C /**/`, because we're *past* the end of the identifier and might want to complete `extends`.
2489                // If `contextToken !== previousToken`, this is `class C ex/**/`.
2490                && !(isClassLike(contextToken.parent) && (contextToken !== previousToken || position > previousToken.end));
2491        }
2492
2493        function isFunctionLikeButNotConstructor(kind: SyntaxKind) {
2494            return isFunctionLikeKind(kind) && kind !== SyntaxKind.Constructor;
2495        }
2496
2497        function isDotOfNumericLiteral(contextToken: Node): boolean {
2498            if (contextToken.kind === SyntaxKind.NumericLiteral) {
2499                const text = contextToken.getFullText();
2500                return text.charAt(text.length - 1) === ".";
2501            }
2502
2503            return false;
2504        }
2505
2506        function isVariableDeclarationListButNotTypeArgument(node: Node): boolean {
2507            return node.parent.kind === SyntaxKind.VariableDeclarationList
2508                && !isPossiblyTypeArgumentPosition(node, sourceFile, typeChecker);
2509        }
2510
2511        /**
2512         * Filters out completion suggestions for named imports or exports.
2513         *
2514         * @returns Symbols to be suggested in an object binding pattern or object literal expression, barring those whose declarations
2515         *          do not occur at the current position and have not otherwise been typed.
2516         */
2517        function filterObjectMembersList(contextualMemberSymbols: Symbol[], existingMembers: readonly Declaration[]): Symbol[] {
2518            if (existingMembers.length === 0) {
2519                return contextualMemberSymbols;
2520            }
2521
2522            const membersDeclaredBySpreadAssignment = new Set<string>();
2523            const existingMemberNames = new Set<__String>();
2524            for (const m of existingMembers) {
2525                // Ignore omitted expressions for missing members
2526                if (m.kind !== SyntaxKind.PropertyAssignment &&
2527                    m.kind !== SyntaxKind.ShorthandPropertyAssignment &&
2528                    m.kind !== SyntaxKind.BindingElement &&
2529                    m.kind !== SyntaxKind.MethodDeclaration &&
2530                    m.kind !== SyntaxKind.GetAccessor &&
2531                    m.kind !== SyntaxKind.SetAccessor &&
2532                    m.kind !== SyntaxKind.SpreadAssignment) {
2533                    continue;
2534                }
2535
2536                // If this is the current item we are editing right now, do not filter it out
2537                if (isCurrentlyEditingNode(m)) {
2538                    continue;
2539                }
2540
2541                let existingName: __String | undefined;
2542
2543                if (isSpreadAssignment(m)) {
2544                    setMembersDeclaredBySpreadAssignment(m, membersDeclaredBySpreadAssignment);
2545                }
2546                else if (isBindingElement(m) && m.propertyName) {
2547                    // include only identifiers in completion list
2548                    if (m.propertyName.kind === SyntaxKind.Identifier) {
2549                        existingName = m.propertyName.escapedText;
2550                    }
2551                }
2552                else {
2553                    // TODO: Account for computed property name
2554                    // NOTE: if one only performs this step when m.name is an identifier,
2555                    // things like '__proto__' are not filtered out.
2556                    const name = getNameOfDeclaration(m);
2557                    existingName = name && isPropertyNameLiteral(name) ? getEscapedTextOfIdentifierOrLiteral(name) : undefined;
2558                }
2559
2560                if (existingName !== undefined) {
2561                    existingMemberNames.add(existingName);
2562                }
2563            }
2564
2565            const filteredSymbols = contextualMemberSymbols.filter(m => !existingMemberNames.has(m.escapedName));
2566            setSortTextToMemberDeclaredBySpreadAssignment(membersDeclaredBySpreadAssignment, filteredSymbols);
2567
2568            return filteredSymbols;
2569        }
2570
2571        function setMembersDeclaredBySpreadAssignment(declaration: SpreadAssignment | JsxSpreadAttribute, membersDeclaredBySpreadAssignment: Set<string>) {
2572            const expression = declaration.expression;
2573            const symbol = typeChecker.getSymbolAtLocation(expression);
2574            const type = symbol && typeChecker.getTypeOfSymbolAtLocation(symbol, expression);
2575            const properties = type && (<ObjectType>type).properties;
2576            if (properties) {
2577                properties.forEach(property => {
2578                    membersDeclaredBySpreadAssignment.add(property.name);
2579                });
2580            }
2581        }
2582
2583        // Set SortText to OptionalMember if it is an optional member
2584        function setSortTextToOptionalMember() {
2585            symbols.forEach(m => {
2586                if (m.flags & SymbolFlags.Optional) {
2587                    symbolToSortTextMap[getSymbolId(m)] = symbolToSortTextMap[getSymbolId(m)] || SortText.OptionalMember;
2588                }
2589            });
2590        }
2591
2592        // Set SortText to MemberDeclaredBySpreadAssignment if it is fulfilled by spread assignment
2593        function setSortTextToMemberDeclaredBySpreadAssignment(membersDeclaredBySpreadAssignment: Set<string>, contextualMemberSymbols: Symbol[]): void {
2594            if (membersDeclaredBySpreadAssignment.size === 0) {
2595                return;
2596            }
2597            for (const contextualMemberSymbol of contextualMemberSymbols) {
2598                if (membersDeclaredBySpreadAssignment.has(contextualMemberSymbol.name)) {
2599                    symbolToSortTextMap[getSymbolId(contextualMemberSymbol)] = SortText.MemberDeclaredBySpreadAssignment;
2600                }
2601            }
2602        }
2603
2604        /**
2605         * Filters out completion suggestions for class elements.
2606         *
2607         * @returns Symbols to be suggested in an class element depending on existing memebers and symbol flags
2608         */
2609        function filterClassMembersList(baseSymbols: readonly Symbol[], existingMembers: readonly ClassElement[], currentClassElementModifierFlags: ModifierFlags): Symbol[] {
2610            const existingMemberNames = new Set<__String>();
2611            for (const m of existingMembers) {
2612                // Ignore omitted expressions for missing members
2613                if (m.kind !== SyntaxKind.PropertyDeclaration &&
2614                    m.kind !== SyntaxKind.MethodDeclaration &&
2615                    m.kind !== SyntaxKind.GetAccessor &&
2616                    m.kind !== SyntaxKind.SetAccessor) {
2617                    continue;
2618                }
2619
2620                // If this is the current item we are editing right now, do not filter it out
2621                if (isCurrentlyEditingNode(m)) {
2622                    continue;
2623                }
2624
2625                // Dont filter member even if the name matches if it is declared private in the list
2626                if (hasEffectiveModifier(m, ModifierFlags.Private)) {
2627                    continue;
2628                }
2629
2630                // do not filter it out if the static presence doesnt match
2631                if (hasEffectiveModifier(m, ModifierFlags.Static) !== !!(currentClassElementModifierFlags & ModifierFlags.Static)) {
2632                    continue;
2633                }
2634
2635                const existingName = getPropertyNameForPropertyNameNode(m.name!);
2636                if (existingName) {
2637                    existingMemberNames.add(existingName);
2638                }
2639            }
2640
2641            return baseSymbols.filter(propertySymbol =>
2642                !existingMemberNames.has(propertySymbol.escapedName) &&
2643                !!propertySymbol.declarations &&
2644                !(getDeclarationModifierFlagsFromSymbol(propertySymbol) & ModifierFlags.Private) &&
2645                !(propertySymbol.valueDeclaration && isPrivateIdentifierPropertyDeclaration(propertySymbol.valueDeclaration)));
2646        }
2647
2648        /**
2649         * Filters out completion suggestions from 'symbols' according to existing JSX attributes.
2650         *
2651         * @returns Symbols to be suggested in a JSX element, barring those whose attributes
2652         *          do not occur at the current position and have not otherwise been typed.
2653         */
2654        function filterJsxAttributes(symbols: Symbol[], attributes: NodeArray<JsxAttribute | JsxSpreadAttribute>): Symbol[] {
2655            const seenNames = new Set<__String>();
2656            const membersDeclaredBySpreadAssignment = new Set<string>();
2657            for (const attr of attributes) {
2658                // If this is the current item we are editing right now, do not filter it out
2659                if (isCurrentlyEditingNode(attr)) {
2660                    continue;
2661                }
2662
2663                if (attr.kind === SyntaxKind.JsxAttribute) {
2664                    seenNames.add(attr.name.escapedText);
2665                }
2666                else if (isJsxSpreadAttribute(attr)) {
2667                    setMembersDeclaredBySpreadAssignment(attr, membersDeclaredBySpreadAssignment);
2668                }
2669            }
2670            const filteredSymbols = symbols.filter(a => !seenNames.has(a.escapedName));
2671
2672            setSortTextToMemberDeclaredBySpreadAssignment(membersDeclaredBySpreadAssignment, filteredSymbols);
2673
2674            return filteredSymbols;
2675        }
2676
2677        function isCurrentlyEditingNode(node: Node): boolean {
2678            return node.getStart(sourceFile) <= position && position <= node.getEnd();
2679        }
2680    }
2681
2682    interface CompletionEntryDisplayNameForSymbol {
2683        readonly name: string;
2684        readonly needsConvertPropertyAccess: boolean;
2685    }
2686    function getCompletionEntryDisplayNameForSymbol(
2687        symbol: Symbol,
2688        target: ScriptTarget,
2689        origin: SymbolOriginInfo | undefined,
2690        kind: CompletionKind,
2691        jsxIdentifierExpected: boolean,
2692    ): CompletionEntryDisplayNameForSymbol | undefined {
2693        const name = originIsExport(origin) ? getNameForExportedSymbol(symbol, target) : symbol.name;
2694        if (name === undefined
2695            // If the symbol is external module, don't show it in the completion list
2696            // (i.e declare module "http" { const x; } | // <= request completion here, "http" should not be there)
2697            || symbol.flags & SymbolFlags.Module && isSingleOrDoubleQuote(name.charCodeAt(0))
2698            // If the symbol is the internal name of an ES symbol, it is not a valid entry. Internal names for ES symbols start with "__@"
2699            || isKnownSymbol(symbol)) {
2700            return undefined;
2701        }
2702
2703        const validNameResult: CompletionEntryDisplayNameForSymbol = { name, needsConvertPropertyAccess: false };
2704        if (isIdentifierText(name, target, jsxIdentifierExpected ? LanguageVariant.JSX : LanguageVariant.Standard) || symbol.valueDeclaration && isPrivateIdentifierPropertyDeclaration(symbol.valueDeclaration)) {
2705            return validNameResult;
2706        }
2707        switch (kind) {
2708            case CompletionKind.MemberLike:
2709                return undefined;
2710            case CompletionKind.ObjectPropertyDeclaration:
2711                // TODO: GH#18169
2712                return { name: JSON.stringify(name), needsConvertPropertyAccess: false };
2713            case CompletionKind.PropertyAccess:
2714            case CompletionKind.Global: // For a 'this.' completion it will be in a global context, but may have a non-identifier name.
2715                // Don't add a completion for a name starting with a space. See https://github.com/Microsoft/TypeScript/pull/20547
2716                return name.charCodeAt(0) === CharacterCodes.space ? undefined : { name, needsConvertPropertyAccess: true };
2717            case CompletionKind.None:
2718            case CompletionKind.String:
2719                return validNameResult;
2720            default:
2721                Debug.assertNever(kind);
2722        }
2723    }
2724
2725    // A cache of completion entries for keywords, these do not change between sessions
2726    const _keywordCompletions: CompletionEntry[][] = [];
2727    const allKeywordsCompletions: () => readonly CompletionEntry[] = memoize(() => {
2728        const res: CompletionEntry[] = [];
2729        for (let i = SyntaxKind.FirstKeyword; i <= SyntaxKind.LastKeyword; i++) {
2730            res.push({
2731                name: tokenToString(i)!,
2732                kind: ScriptElementKind.keyword,
2733                kindModifiers: ScriptElementKindModifier.none,
2734                sortText: SortText.GlobalsOrKeywords
2735            });
2736        }
2737        return res;
2738    });
2739
2740    function getKeywordCompletions(keywordFilter: KeywordCompletionFilters, filterOutTsOnlyKeywords: boolean): readonly CompletionEntry[] {
2741        if (!filterOutTsOnlyKeywords) return getTypescriptKeywordCompletions(keywordFilter);
2742
2743        const index = keywordFilter + KeywordCompletionFilters.Last + 1;
2744        return _keywordCompletions[index] ||
2745            (_keywordCompletions[index] = getTypescriptKeywordCompletions(keywordFilter)
2746                .filter(entry => !isTypeScriptOnlyKeyword(stringToToken(entry.name)!))
2747            );
2748    }
2749
2750    function getTypescriptKeywordCompletions(keywordFilter: KeywordCompletionFilters): readonly CompletionEntry[] {
2751        return _keywordCompletions[keywordFilter] || (_keywordCompletions[keywordFilter] = allKeywordsCompletions().filter(entry => {
2752            const kind = stringToToken(entry.name)!;
2753            switch (keywordFilter) {
2754                case KeywordCompletionFilters.None:
2755                    return false;
2756                case KeywordCompletionFilters.All:
2757                    return isFunctionLikeBodyKeyword(kind)
2758                        || kind === SyntaxKind.DeclareKeyword
2759                        || kind === SyntaxKind.ModuleKeyword
2760                        || kind === SyntaxKind.TypeKeyword
2761                        || kind === SyntaxKind.NamespaceKeyword
2762                        || isTypeKeyword(kind) && kind !== SyntaxKind.UndefinedKeyword;
2763                case KeywordCompletionFilters.FunctionLikeBodyKeywords:
2764                    return isFunctionLikeBodyKeyword(kind);
2765                case KeywordCompletionFilters.ClassElementKeywords:
2766                    return isClassMemberCompletionKeyword(kind);
2767                case KeywordCompletionFilters.InterfaceElementKeywords:
2768                    return isInterfaceOrTypeLiteralCompletionKeyword(kind);
2769                case KeywordCompletionFilters.ConstructorParameterKeywords:
2770                    return isParameterPropertyModifier(kind);
2771                case KeywordCompletionFilters.TypeAssertionKeywords:
2772                    return isTypeKeyword(kind) || kind === SyntaxKind.ConstKeyword;
2773                case KeywordCompletionFilters.TypeKeywords:
2774                    return isTypeKeyword(kind);
2775                default:
2776                    return Debug.assertNever(keywordFilter);
2777            }
2778        }));
2779    }
2780
2781    function isTypeScriptOnlyKeyword(kind: SyntaxKind) {
2782        switch (kind) {
2783            case SyntaxKind.AbstractKeyword:
2784            case SyntaxKind.AnyKeyword:
2785            case SyntaxKind.BigIntKeyword:
2786            case SyntaxKind.BooleanKeyword:
2787            case SyntaxKind.DeclareKeyword:
2788            case SyntaxKind.EnumKeyword:
2789            case SyntaxKind.GlobalKeyword:
2790            case SyntaxKind.ImplementsKeyword:
2791            case SyntaxKind.InferKeyword:
2792            case SyntaxKind.InterfaceKeyword:
2793            case SyntaxKind.IsKeyword:
2794            case SyntaxKind.KeyOfKeyword:
2795            case SyntaxKind.ModuleKeyword:
2796            case SyntaxKind.NamespaceKeyword:
2797            case SyntaxKind.NeverKeyword:
2798            case SyntaxKind.NumberKeyword:
2799            case SyntaxKind.ObjectKeyword:
2800            case SyntaxKind.PrivateKeyword:
2801            case SyntaxKind.ProtectedKeyword:
2802            case SyntaxKind.PublicKeyword:
2803            case SyntaxKind.ReadonlyKeyword:
2804            case SyntaxKind.StringKeyword:
2805            case SyntaxKind.SymbolKeyword:
2806            case SyntaxKind.TypeKeyword:
2807            case SyntaxKind.UniqueKeyword:
2808            case SyntaxKind.UnknownKeyword:
2809                return true;
2810            default:
2811                return false;
2812        }
2813    }
2814
2815    function isInterfaceOrTypeLiteralCompletionKeyword(kind: SyntaxKind): boolean {
2816        return kind === SyntaxKind.ReadonlyKeyword;
2817    }
2818
2819    function isClassMemberCompletionKeyword(kind: SyntaxKind) {
2820        switch (kind) {
2821            case SyntaxKind.AbstractKeyword:
2822            case SyntaxKind.ConstructorKeyword:
2823            case SyntaxKind.GetKeyword:
2824            case SyntaxKind.SetKeyword:
2825            case SyntaxKind.AsyncKeyword:
2826            case SyntaxKind.DeclareKeyword:
2827                return true;
2828            default:
2829                return isClassMemberModifier(kind);
2830        }
2831    }
2832
2833    function isFunctionLikeBodyKeyword(kind: SyntaxKind) {
2834        return kind === SyntaxKind.AsyncKeyword
2835            || kind === SyntaxKind.AwaitKeyword
2836            || kind === SyntaxKind.AsKeyword
2837            || !isContextualKeyword(kind) && !isClassMemberCompletionKeyword(kind);
2838    }
2839
2840    function keywordForNode(node: Node): SyntaxKind {
2841        return isIdentifier(node) ? node.originalKeywordKind || SyntaxKind.Unknown : node.kind;
2842    }
2843
2844    /** Get the corresponding JSDocTag node if the position is in a jsDoc comment */
2845    function getJsDocTagAtPosition(node: Node, position: number): JSDocTag | undefined {
2846        const jsdoc = findAncestor(node, isJSDoc);
2847        return jsdoc && jsdoc.tags && (rangeContainsPosition(jsdoc, position) ? findLast(jsdoc.tags, tag => tag.pos < position) : undefined);
2848    }
2849
2850    export function getPropertiesForObjectExpression(contextualType: Type, completionsType: Type | undefined, obj: ObjectLiteralExpression | JsxAttributes, checker: TypeChecker): Symbol[] {
2851        const hasCompletionsType = completionsType && completionsType !== contextualType;
2852        const type = hasCompletionsType && !(completionsType!.flags & TypeFlags.AnyOrUnknown)
2853            ? checker.getUnionType([contextualType, completionsType!])
2854            : contextualType;
2855
2856        const properties = type.isUnion()
2857            ? checker.getAllPossiblePropertiesOfTypes(type.types.filter(memberType =>
2858                // If we're providing completions for an object literal, skip primitive, array-like, or callable types since those shouldn't be implemented by object literals.
2859                !(memberType.flags & TypeFlags.Primitive ||
2860                    checker.isArrayLikeType(memberType) ||
2861                    typeHasCallOrConstructSignatures(memberType, checker) ||
2862                    checker.isTypeInvalidDueToUnionDiscriminant(memberType, obj))))
2863            : type.getApparentProperties();
2864
2865        return hasCompletionsType ? properties.filter(hasDeclarationOtherThanSelf) : properties;
2866
2867        // Filter out members whose only declaration is the object literal itself to avoid
2868        // self-fulfilling completions like:
2869        //
2870        // function f<T>(x: T) {}
2871        // f({ abc/**/: "" }) // `abc` is a member of `T` but only because it declares itself
2872        function hasDeclarationOtherThanSelf(member: Symbol) {
2873            return some(member.declarations, decl => decl.parent !== obj);
2874        }
2875    }
2876
2877    /**
2878     * Gets all properties on a type, but if that type is a union of several types,
2879     * excludes array-like types or callable/constructable types.
2880     */
2881    function getPropertiesForCompletion(type: Type, checker: TypeChecker): Symbol[] {
2882        return type.isUnion()
2883            ? Debug.checkEachDefined(checker.getAllPossiblePropertiesOfTypes(type.types), "getAllPossiblePropertiesOfTypes() should all be defined")
2884            : Debug.checkEachDefined(type.getApparentProperties(), "getApparentProperties() should all be defined");
2885    }
2886
2887    /**
2888     * Returns the immediate owning class declaration of a context token,
2889     * on the condition that one exists and that the context implies completion should be given.
2890     */
2891    function tryGetObjectTypeDeclarationCompletionContainer(sourceFile: SourceFile, contextToken: Node | undefined, location: Node, position: number): ObjectTypeDeclaration | undefined {
2892        // class c { method() { } | method2() { } }
2893        switch (location.kind) {
2894            case SyntaxKind.SyntaxList:
2895                return tryCast(location.parent, isObjectTypeDeclaration);
2896            case SyntaxKind.EndOfFileToken:
2897                const cls = tryCast(lastOrUndefined(cast(location.parent, isSourceFile).statements), isObjectTypeDeclaration);
2898                if (cls && !findChildOfKind(cls, SyntaxKind.CloseBraceToken, sourceFile)) {
2899                    return cls;
2900                }
2901                break;
2902            case SyntaxKind.Identifier: {
2903                // class c { public prop = c| }
2904                if (isPropertyDeclaration(location.parent) && location.parent.initializer === location) {
2905                    return undefined;
2906                }
2907                // class c extends React.Component { a: () => 1\n compon| }
2908                if (isFromObjectTypeDeclaration(location)) {
2909                    return findAncestor(location, isObjectTypeDeclaration);
2910                }
2911            }
2912        }
2913
2914        if (!contextToken) return undefined;
2915
2916        switch (contextToken.kind) {
2917            case SyntaxKind.EqualsToken: // class c { public prop = | /* global completions */ }
2918                return undefined;
2919
2920            case SyntaxKind.SemicolonToken: // class c {getValue(): number; | }
2921            case SyntaxKind.CloseBraceToken: // class c { method() { } | }
2922                // class c { method() { } b| }
2923                return isFromObjectTypeDeclaration(location) && (location.parent as ClassElement | TypeElement).name === location
2924                    ? location.parent.parent as ObjectTypeDeclaration
2925                    : tryCast(location, isObjectTypeDeclaration);
2926            case SyntaxKind.OpenBraceToken: // class c { |
2927            case SyntaxKind.CommaToken: // class c {getValue(): number, | }
2928                return tryCast(contextToken.parent, isObjectTypeDeclaration);
2929            default:
2930                if (!isFromObjectTypeDeclaration(contextToken)) {
2931                    // class c extends React.Component { a: () => 1\n| }
2932                    if (getLineAndCharacterOfPosition(sourceFile, contextToken.getEnd()).line !== getLineAndCharacterOfPosition(sourceFile, position).line && isObjectTypeDeclaration(location)) {
2933                        return location;
2934                    }
2935                    return undefined;
2936                }
2937                const isValidKeyword = isClassLike(contextToken.parent.parent) ? isClassMemberCompletionKeyword : isInterfaceOrTypeLiteralCompletionKeyword;
2938                return (isValidKeyword(contextToken.kind) || contextToken.kind === SyntaxKind.AsteriskToken || isIdentifier(contextToken) && isValidKeyword(stringToToken(contextToken.text)!)) // TODO: GH#18217
2939                    ? contextToken.parent.parent as ObjectTypeDeclaration : undefined;
2940        }
2941    }
2942
2943    // TODO: GH#19856 Would like to return `node is Node & { parent: (ClassElement | TypeElement) & { parent: ObjectTypeDeclaration } }` but then compilation takes > 10 minutes
2944    function isFromObjectTypeDeclaration(node: Node): boolean {
2945        return node.parent && isClassOrTypeElement(node.parent) && isObjectTypeDeclaration(node.parent.parent);
2946    }
2947
2948    function isValidTrigger(sourceFile: SourceFile, triggerCharacter: CompletionsTriggerCharacter, contextToken: Node | undefined, position: number): boolean {
2949        switch (triggerCharacter) {
2950            case ".":
2951            case "@":
2952                return true;
2953            case '"':
2954            case "'":
2955            case "`":
2956                // Only automatically bring up completions if this is an opening quote.
2957                return !!contextToken && isStringLiteralOrTemplate(contextToken) && position === contextToken.getStart(sourceFile) + 1;
2958            case "#":
2959                return !!contextToken && isPrivateIdentifier(contextToken) && !!getContainingClass(contextToken);
2960            case "<":
2961                // Opening JSX tag
2962                return !!contextToken && contextToken.kind === SyntaxKind.LessThanToken && (!isBinaryExpression(contextToken.parent) || binaryExpressionMayBeOpenTag(contextToken.parent));
2963            case "/":
2964                return !!contextToken && (isStringLiteralLike(contextToken)
2965                    ? !!tryGetImportFromModuleSpecifier(contextToken)
2966                    : contextToken.kind === SyntaxKind.SlashToken && isJsxClosingElement(contextToken.parent));
2967            default:
2968                return Debug.assertNever(triggerCharacter);
2969        }
2970    }
2971
2972    function binaryExpressionMayBeOpenTag({ left }: BinaryExpression): boolean {
2973        return nodeIsMissing(left);
2974    }
2975
2976    function findAlias(typeChecker: TypeChecker, symbol: Symbol, predicate: (symbol: Symbol) => boolean): Symbol | undefined {
2977        let currentAlias: Symbol | undefined = symbol;
2978        while (currentAlias.flags & SymbolFlags.Alias && (currentAlias = typeChecker.getImmediateAliasedSymbol(currentAlias))) {
2979            if (predicate(currentAlias)) {
2980                return currentAlias;
2981            }
2982        }
2983    }
2984
2985    /** Determines if a type is exactly the same type resolved by the global 'self', 'global', or 'globalThis'. */
2986    function isProbablyGlobalType(type: Type, sourceFile: SourceFile, checker: TypeChecker) {
2987        // The type of `self` and `window` is the same in lib.dom.d.ts, but `window` does not exist in
2988        // lib.webworker.d.ts, so checking against `self` is also a check against `window` when it exists.
2989        const selfSymbol = checker.resolveName("self", /*location*/ undefined, SymbolFlags.Value, /*excludeGlobals*/ false);
2990        if (selfSymbol && checker.getTypeOfSymbolAtLocation(selfSymbol, sourceFile) === type) {
2991            return true;
2992        }
2993        const globalSymbol = checker.resolveName("global", /*location*/ undefined, SymbolFlags.Value, /*excludeGlobals*/ false);
2994        if (globalSymbol && checker.getTypeOfSymbolAtLocation(globalSymbol, sourceFile) === type) {
2995            return true;
2996        }
2997        const globalThisSymbol = checker.resolveName("globalThis", /*location*/ undefined, SymbolFlags.Value, /*excludeGlobals*/ false);
2998        if (globalThisSymbol && checker.getTypeOfSymbolAtLocation(globalThisSymbol, sourceFile) === type) {
2999            return true;
3000        }
3001        return false;
3002    }
3003
3004    function isStaticProperty(symbol: Symbol) {
3005        return !!(symbol.valueDeclaration && getEffectiveModifierFlags(symbol.valueDeclaration) & ModifierFlags.Static && isClassLike(symbol.valueDeclaration.parent));
3006    }
3007
3008    function tryGetObjectLiteralContextualType(node: ObjectLiteralExpression, typeChecker: TypeChecker) {
3009        const type = typeChecker.getContextualType(node);
3010        if (type) {
3011            return type;
3012        }
3013        if (isBinaryExpression(node.parent) && node.parent.operatorToken.kind === SyntaxKind.EqualsToken) {
3014            return typeChecker.getTypeAtLocation(node.parent);
3015        }
3016        return undefined;
3017    }
3018}
3019