1/* @internal */
2namespace ts.Completions {
3    // Exported only for tests
4    export const moduleSpecifierResolutionLimit = 100;
5    export const moduleSpecifierResolutionCacheAttemptLimit = 1000;
6
7    export type Log = (message: string) => void;
8
9    export type SortText = string & { __sortText: any };
10    export const SortText = {
11        // Presets
12        LocalDeclarationPriority: "10" as SortText,
13        LocationPriority: "11" as SortText,
14        OptionalMember: "12" as SortText,
15        MemberDeclaredBySpreadAssignment: "13" as SortText,
16        SuggestedClassMembers: "14" as SortText,
17        GlobalsOrKeywords: "15" as SortText,
18        AutoImportSuggestions: "16" as SortText,
19        ClassMemberSnippets: "17" as SortText,
20        JavascriptIdentifiers: "18" as SortText,
21
22        // Transformations
23        Deprecated(sortText: SortText): SortText {
24            return "z" + sortText as SortText;
25        },
26
27        ObjectLiteralProperty(presetSortText: SortText, symbolDisplayName: string): SortText {
28            return `${presetSortText}\0${symbolDisplayName}\0` as SortText;
29        },
30
31        SortBelow(sortText: SortText): SortText {
32            return sortText + "1" as SortText;
33        },
34    };
35
36    /**
37     * Special values for `CompletionInfo['source']` used to disambiguate
38     * completion items with the same `name`. (Each completion item must
39     * have a unique name/source combination, because those two fields
40     * comprise `CompletionEntryIdentifier` in `getCompletionEntryDetails`.
41     *
42     * When the completion item is an auto-import suggestion, the source
43     * is the module specifier of the suggestion. To avoid collisions,
44     * the values here should not be a module specifier we would ever
45     * generate for an auto-import.
46     */
47    export enum CompletionSource {
48        /** Completions that require `this.` insertion text */
49        ThisProperty = "ThisProperty/",
50        /** Auto-import that comes attached to a class member snippet */
51        ClassMemberSnippet = "ClassMemberSnippet/",
52        /** A type-only import that needs to be promoted in order to be used at the completion location */
53        TypeOnlyAlias = "TypeOnlyAlias/",
54        /** Auto-import that comes attached to an object literal method snippet */
55        ObjectLiteralMethodSnippet = "ObjectLiteralMethodSnippet/",
56    }
57
58    const enum SymbolOriginInfoKind {
59        ThisType            = 1 << 0,
60        SymbolMember        = 1 << 1,
61        Export              = 1 << 2,
62        Promise             = 1 << 3,
63        Nullable            = 1 << 4,
64        ResolvedExport      = 1 << 5,
65        TypeOnlyAlias       = 1 << 6,
66        ObjectLiteralMethod = 1 << 7,
67
68        SymbolMemberNoExport = SymbolMember,
69        SymbolMemberExport = SymbolMember | Export,
70    }
71
72    interface SymbolOriginInfo {
73        kind: SymbolOriginInfoKind;
74        isDefaultExport?: boolean;
75        isFromPackageJson?: boolean;
76        fileName?: string;
77    }
78
79    interface SymbolOriginInfoExport extends SymbolOriginInfo {
80        symbolName: string;
81        moduleSymbol: Symbol;
82        isDefaultExport: boolean;
83        exportName: string;
84        exportMapKey: string;
85    }
86
87    interface SymbolOriginInfoResolvedExport extends SymbolOriginInfo {
88        symbolName: string;
89        moduleSymbol: Symbol;
90        exportName: string;
91        moduleSpecifier: string;
92    }
93
94    interface SymbolOriginInfoTypeOnlyAlias extends SymbolOriginInfo {
95        declaration: TypeOnlyAliasDeclaration;
96    }
97
98    interface SymbolOriginInfoObjectLiteralMethod extends SymbolOriginInfo {
99        insertText: string,
100        labelDetails: CompletionEntryLabelDetails,
101        isSnippet?: true,
102    }
103
104    function originIsThisType(origin: SymbolOriginInfo): boolean {
105        return !!(origin.kind & SymbolOriginInfoKind.ThisType);
106    }
107
108    function originIsSymbolMember(origin: SymbolOriginInfo): boolean {
109        return !!(origin.kind & SymbolOriginInfoKind.SymbolMember);
110    }
111
112    function originIsExport(origin: SymbolOriginInfo | undefined): origin is SymbolOriginInfoExport {
113        return !!(origin && origin.kind & SymbolOriginInfoKind.Export);
114    }
115
116    function originIsResolvedExport(origin: SymbolOriginInfo | undefined): origin is SymbolOriginInfoResolvedExport {
117        return !!(origin && origin.kind === SymbolOriginInfoKind.ResolvedExport);
118    }
119
120    function originIncludesSymbolName(origin: SymbolOriginInfo | undefined): origin is SymbolOriginInfoExport | SymbolOriginInfoResolvedExport {
121        return originIsExport(origin) || originIsResolvedExport(origin);
122    }
123
124    function originIsPackageJsonImport(origin: SymbolOriginInfo | undefined): origin is SymbolOriginInfoExport {
125        return (originIsExport(origin) || originIsResolvedExport(origin)) && !!origin.isFromPackageJson;
126    }
127
128    function originIsPromise(origin: SymbolOriginInfo): boolean {
129        return !!(origin.kind & SymbolOriginInfoKind.Promise);
130    }
131
132    function originIsNullableMember(origin: SymbolOriginInfo): boolean {
133        return !!(origin.kind & SymbolOriginInfoKind.Nullable);
134    }
135
136    function originIsTypeOnlyAlias(origin: SymbolOriginInfo | undefined): origin is SymbolOriginInfoTypeOnlyAlias {
137        return !!(origin && origin.kind & SymbolOriginInfoKind.TypeOnlyAlias);
138    }
139
140    function originIsObjectLiteralMethod(origin: SymbolOriginInfo | undefined): origin is SymbolOriginInfoObjectLiteralMethod {
141        return !!(origin && origin.kind & SymbolOriginInfoKind.ObjectLiteralMethod);
142    }
143
144    interface UniqueNameSet {
145        add(name: string): void;
146        has(name: string): boolean;
147    }
148
149    /**
150     * Map from symbol index in `symbols` -> SymbolOriginInfo.
151     */
152    type SymbolOriginInfoMap = Record<number, SymbolOriginInfo>;
153
154    /** Map from symbol id -> SortText. */
155    type SymbolSortTextMap = (SortText | undefined)[];
156
157    const enum KeywordCompletionFilters {
158        None,                           // No keywords
159        All,                            // Every possible keyword (TODO: This is never appropriate)
160        ClassElementKeywords,           // Keywords inside class body
161        InterfaceElementKeywords,       // Keywords inside interface body
162        ConstructorParameterKeywords,   // Keywords at constructor parameter
163        FunctionLikeBodyKeywords,       // Keywords at function like body
164        TypeAssertionKeywords,
165        TypeKeywords,
166        TypeKeyword,                    // Literally just `type`
167        Last = TypeKeyword
168    }
169
170    const enum GlobalsSearch { Continue, Success, Fail }
171
172    interface ModuleSpecifierResolutioContext {
173        tryResolve: (exportInfo: readonly SymbolExportInfo[], symbolName: string, isFromAmbientModule: boolean) => ModuleSpecifierResolutionResult;
174        resolvedAny: () => boolean;
175        skippedAny: () => boolean;
176        resolvedBeyondLimit: () => boolean;
177    }
178
179    type ModuleSpecifierResolutionResult = "skipped" | "failed" | {
180        exportInfo?: SymbolExportInfo;
181        moduleSpecifier: string;
182    };
183
184    function resolvingModuleSpecifiers<TReturn>(
185        logPrefix: string,
186        host: LanguageServiceHost,
187        resolver: codefix.ImportSpecifierResolver,
188        program: Program,
189        position: number,
190        preferences: UserPreferences,
191        isForImportStatementCompletion: boolean,
192        isValidTypeOnlyUseSite: boolean,
193        cb: (context: ModuleSpecifierResolutioContext) => TReturn,
194    ): TReturn {
195        const start = timestamp();
196        // Under `--moduleResolution nodenext`, we have to resolve module specifiers up front, because
197        // package.json exports can mean we *can't* resolve a module specifier (that doesn't include a
198        // relative path into node_modules), and we want to filter those completions out entirely.
199        // Import statement completions always need specifier resolution because the module specifier is
200        // part of their `insertText`, not the `codeActions` creating edits away from the cursor.
201        const needsFullResolution = isForImportStatementCompletion || moduleResolutionRespectsExports(getEmitModuleResolutionKind(program.getCompilerOptions()));
202        let skippedAny = false;
203        let ambientCount = 0;
204        let resolvedCount = 0;
205        let resolvedFromCacheCount = 0;
206        let cacheAttemptCount = 0;
207
208        const result = cb({
209            tryResolve,
210            skippedAny: () => skippedAny,
211            resolvedAny: () => resolvedCount > 0,
212            resolvedBeyondLimit: () => resolvedCount > moduleSpecifierResolutionLimit,
213        });
214
215        const hitRateMessage = cacheAttemptCount ? ` (${(resolvedFromCacheCount / cacheAttemptCount * 100).toFixed(1)}% hit rate)` : "";
216        host.log?.(`${logPrefix}: resolved ${resolvedCount} module specifiers, plus ${ambientCount} ambient and ${resolvedFromCacheCount} from cache${hitRateMessage}`);
217        host.log?.(`${logPrefix}: response is ${skippedAny ? "incomplete" : "complete"}`);
218        host.log?.(`${logPrefix}: ${timestamp() - start}`);
219        return result;
220
221        function tryResolve(exportInfo: readonly SymbolExportInfo[], symbolName: string, isFromAmbientModule: boolean): ModuleSpecifierResolutionResult {
222            if (isFromAmbientModule) {
223                const result = resolver.getModuleSpecifierForBestExportInfo(exportInfo, symbolName, position, isValidTypeOnlyUseSite);
224                if (result) {
225                    ambientCount++;
226                }
227                return result || "failed";
228            }
229            const shouldResolveModuleSpecifier = needsFullResolution || preferences.allowIncompleteCompletions && resolvedCount < moduleSpecifierResolutionLimit;
230            const shouldGetModuleSpecifierFromCache = !shouldResolveModuleSpecifier && preferences.allowIncompleteCompletions && cacheAttemptCount < moduleSpecifierResolutionCacheAttemptLimit;
231            const result = (shouldResolveModuleSpecifier || shouldGetModuleSpecifierFromCache)
232                ? resolver.getModuleSpecifierForBestExportInfo(exportInfo, symbolName, position, isValidTypeOnlyUseSite, shouldGetModuleSpecifierFromCache)
233                : undefined;
234
235            if (!shouldResolveModuleSpecifier && !shouldGetModuleSpecifierFromCache || shouldGetModuleSpecifierFromCache && !result) {
236                skippedAny = true;
237            }
238
239            resolvedCount += result?.computedWithoutCacheCount || 0;
240            resolvedFromCacheCount += exportInfo.length - (result?.computedWithoutCacheCount || 0);
241            if (shouldGetModuleSpecifierFromCache) {
242                cacheAttemptCount++;
243            }
244
245            return result || (needsFullResolution ? "failed" : "skipped");
246        }
247    }
248
249    export function getCompletionsAtPosition(
250        host: LanguageServiceHost,
251        program: Program,
252        log: Log,
253        sourceFile: SourceFile,
254        position: number,
255        preferences: UserPreferences,
256        triggerCharacter: CompletionsTriggerCharacter | undefined,
257        completionKind: CompletionTriggerKind | undefined,
258        cancellationToken: CancellationToken,
259        formatContext?: formatting.FormatContext,
260    ): CompletionInfo | undefined {
261        const { previousToken } = getRelevantTokens(position, sourceFile);
262        if (triggerCharacter && !isInString(sourceFile, position, previousToken) && !isValidTrigger(sourceFile, triggerCharacter, previousToken, position)) {
263            return undefined;
264        }
265
266        if (triggerCharacter === " ") {
267            // `isValidTrigger` ensures we are at `import |`
268            if (preferences.includeCompletionsForImportStatements && preferences.includeCompletionsWithInsertText) {
269                return { isGlobalCompletion: true, isMemberCompletion: false, isNewIdentifierLocation: true, isIncomplete: true, entries: [] };
270            }
271            return undefined;
272
273        }
274
275        // If the request is a continuation of an earlier `isIncomplete` response,
276        // we can continue it from the cached previous response.
277        const compilerOptions = program.getCompilerOptions();
278        const incompleteCompletionsCache = preferences.allowIncompleteCompletions ? host.getIncompleteCompletionsCache?.() : undefined;
279        if (incompleteCompletionsCache && completionKind === CompletionTriggerKind.TriggerForIncompleteCompletions && previousToken && isIdentifier(previousToken)) {
280            const incompleteContinuation = continuePreviousIncompleteResponse(incompleteCompletionsCache, sourceFile, previousToken, program, host, preferences, cancellationToken);
281            if (incompleteContinuation) {
282                return incompleteContinuation;
283            }
284        }
285        else {
286            incompleteCompletionsCache?.clear();
287        }
288
289        const stringCompletions = StringCompletions.getStringLiteralCompletions(sourceFile, position, previousToken, compilerOptions, host, program, log, preferences);
290        if (stringCompletions) {
291            return stringCompletions;
292        }
293
294        if (previousToken && isBreakOrContinueStatement(previousToken.parent)
295            && (previousToken.kind === SyntaxKind.BreakKeyword || previousToken.kind === SyntaxKind.ContinueKeyword || previousToken.kind === SyntaxKind.Identifier)) {
296            return getLabelCompletionAtPosition(previousToken.parent);
297        }
298
299        const completionData = getCompletionData(program, log, sourceFile, compilerOptions, position, preferences, /*detailsEntryId*/ undefined, host, formatContext, cancellationToken);
300        if (!completionData) {
301            return undefined;
302        }
303
304        switch (completionData.kind) {
305            case CompletionDataKind.Data:
306                const response = completionInfoFromData(sourceFile, host, program, compilerOptions, log, completionData, preferences, formatContext, position);
307                if (response?.isIncomplete) {
308                    incompleteCompletionsCache?.set(response);
309                }
310                return response;
311            case CompletionDataKind.JsDocTagName:
312                // If the current position is a jsDoc tag name, only tag names should be provided for completion
313                return jsdocCompletionInfo(JsDoc.getJSDocTagNameCompletions());
314            case CompletionDataKind.JsDocTag:
315                // If the current position is a jsDoc tag, only tags should be provided for completion
316                return jsdocCompletionInfo(JsDoc.getJSDocTagCompletions());
317            case CompletionDataKind.JsDocParameterName:
318                return jsdocCompletionInfo(JsDoc.getJSDocParameterNameCompletions(completionData.tag));
319            case CompletionDataKind.Keywords:
320                return specificKeywordCompletionInfo(completionData.keywordCompletions, completionData.isNewIdentifierLocation);
321            default:
322                return Debug.assertNever(completionData);
323        }
324    }
325
326    // Editors will use the `sortText` and then fall back to `name` for sorting, but leave ties in response order.
327    // So, it's important that we sort those ties in the order we want them displayed if it matters. We don't
328    // strictly need to sort by name or SortText here since clients are going to do it anyway, but we have to
329    // do the work of comparing them so we can sort those ties appropriately; plus, it makes the order returned
330    // by the language service consistent with what TS Server does and what editors typically do. This also makes
331    // completions tests make more sense. We used to sort only alphabetically and only in the server layer, but
332    // this made tests really weird, since most fourslash tests don't use the server.
333    function compareCompletionEntries(entryInArray: CompletionEntry, entryToInsert: CompletionEntry): Comparison {
334        let result = compareStringsCaseSensitiveUI(entryInArray.sortText, entryToInsert.sortText);
335        if (result === Comparison.EqualTo) {
336            result = compareStringsCaseSensitiveUI(entryInArray.name, entryToInsert.name);
337        }
338        if (result === Comparison.EqualTo && entryInArray.data?.moduleSpecifier && entryToInsert.data?.moduleSpecifier) {
339            // Sort same-named auto-imports by module specifier
340            result = compareNumberOfDirectorySeparators(
341                (entryInArray.data as CompletionEntryDataResolved).moduleSpecifier,
342                (entryToInsert.data as CompletionEntryDataResolved).moduleSpecifier,
343            );
344        }
345        if (result === Comparison.EqualTo) {
346            // Fall back to symbol order - if we return `EqualTo`, `insertSorted` will put later symbols first.
347            return Comparison.LessThan;
348        }
349        return result;
350    }
351
352    function completionEntryDataIsResolved(data: CompletionEntryDataAutoImport | undefined): data is CompletionEntryDataResolved {
353        return !!data?.moduleSpecifier;
354    }
355
356    function continuePreviousIncompleteResponse(
357        cache: IncompleteCompletionsCache,
358        file: SourceFile,
359        location: Identifier,
360        program: Program,
361        host: LanguageServiceHost,
362        preferences: UserPreferences,
363        cancellationToken: CancellationToken,
364    ): CompletionInfo | undefined {
365        const previousResponse = cache.get();
366        if (!previousResponse) return undefined;
367
368        const lowerCaseTokenText = location.text.toLowerCase();
369        const exportMap = getExportInfoMap(file, host, program, preferences, cancellationToken);
370        const newEntries = resolvingModuleSpecifiers(
371            "continuePreviousIncompleteResponse",
372            host,
373            codefix.createImportSpecifierResolver(file, program, host, preferences),
374            program,
375            location.getStart(),
376            preferences,
377            /*isForImportStatementCompletion*/ false,
378            isValidTypeOnlyAliasUseSite(location),
379            context => {
380                const entries = mapDefined(previousResponse.entries, entry => {
381                    if (!entry.hasAction || !entry.source || !entry.data || completionEntryDataIsResolved(entry.data)) {
382                        // Not an auto import or already resolved; keep as is
383                        return entry;
384                    }
385                    if (!charactersFuzzyMatchInString(entry.name, lowerCaseTokenText)) {
386                        // No longer matches typed characters; filter out
387                        return undefined;
388                    }
389
390                    const { origin } = Debug.checkDefined(getAutoImportSymbolFromCompletionEntryData(entry.name, entry.data, program, host));
391                    const info = exportMap.get(file.path, entry.data.exportMapKey);
392
393                    const result = info && context.tryResolve(info, entry.name, !isExternalModuleNameRelative(stripQuotes(origin.moduleSymbol.name)));
394                    if (result === "skipped") return entry;
395                    if (!result || result === "failed") {
396                        host.log?.(`Unexpected failure resolving auto import for '${entry.name}' from '${entry.source}'`);
397                        return undefined;
398                    }
399
400                    const newOrigin: SymbolOriginInfoResolvedExport = {
401                        ...origin,
402                        kind: SymbolOriginInfoKind.ResolvedExport,
403                        moduleSpecifier: result.moduleSpecifier,
404                    };
405                    // Mutating for performance... feels sketchy but nobody else uses the cache,
406                    // so why bother allocating a bunch of new objects?
407                    entry.data = originToCompletionEntryData(newOrigin);
408                    entry.source = getSourceFromOrigin(newOrigin);
409                    entry.sourceDisplay = [textPart(newOrigin.moduleSpecifier)];
410                    return entry;
411                });
412
413                if (!context.skippedAny()) {
414                    previousResponse.isIncomplete = undefined;
415                }
416
417                return entries;
418            },
419        );
420
421        previousResponse.entries = newEntries;
422        previousResponse.flags = (previousResponse.flags || 0) | CompletionInfoFlags.IsContinuation;
423        return previousResponse;
424    }
425
426    function jsdocCompletionInfo(entries: CompletionEntry[]): CompletionInfo {
427        return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, entries };
428    }
429
430    function keywordToCompletionEntry(keyword: TokenSyntaxKind) {
431        return {
432            name: tokenToString(keyword)!,
433            kind: ScriptElementKind.keyword,
434            kindModifiers: ScriptElementKindModifier.none,
435            sortText: SortText.GlobalsOrKeywords,
436        };
437    }
438
439    function specificKeywordCompletionInfo(entries: readonly CompletionEntry[], isNewIdentifierLocation: boolean): CompletionInfo {
440        return {
441            isGlobalCompletion: false,
442            isMemberCompletion: false,
443            isNewIdentifierLocation,
444            entries: entries.slice(),
445        };
446    }
447
448    function keywordCompletionData(keywordFilters: KeywordCompletionFilters, filterOutTsOnlyKeywords: boolean, isNewIdentifierLocation: boolean): Request {
449        return {
450            kind: CompletionDataKind.Keywords,
451            keywordCompletions: getKeywordCompletions(keywordFilters, filterOutTsOnlyKeywords),
452            isNewIdentifierLocation,
453        };
454    }
455
456    function keywordFiltersFromSyntaxKind(keywordCompletion: TokenSyntaxKind): KeywordCompletionFilters {
457        switch (keywordCompletion) {
458            case SyntaxKind.TypeKeyword: return KeywordCompletionFilters.TypeKeyword;
459            default: Debug.fail("Unknown mapping from SyntaxKind to KeywordCompletionFilters");
460        }
461    }
462
463    function getOptionalReplacementSpan(location: Node | undefined) {
464        // StringLiteralLike locations are handled separately in stringCompletions.ts
465        return location?.kind === SyntaxKind.Identifier ? createTextSpanFromNode(location) : undefined;
466    }
467
468    function completionInfoFromData(
469        sourceFile: SourceFile,
470        host: LanguageServiceHost,
471        program: Program,
472        compilerOptions: CompilerOptions,
473        log: Log,
474        completionData: CompletionData,
475        preferences: UserPreferences,
476        formatContext: formatting.FormatContext | undefined,
477        position: number
478    ): CompletionInfo | undefined {
479        const {
480            symbols,
481            contextToken,
482            completionKind,
483            isInSnippetScope,
484            isNewIdentifierLocation,
485            location,
486            propertyAccessToConvert,
487            keywordFilters,
488            literals,
489            symbolToOriginInfoMap,
490            recommendedCompletion,
491            isJsxInitializer,
492            isTypeOnlyLocation,
493            isJsxIdentifierExpected,
494            isRightOfOpenTag,
495            importStatementCompletion,
496            insideJsDocTagTypeExpression,
497            symbolToSortTextMap: symbolToSortTextMap,
498            hasUnresolvedAutoImports,
499        } = completionData;
500
501        // Verify if the file is JSX language variant
502        if (getLanguageVariant(sourceFile.scriptKind) === LanguageVariant.JSX) {
503            const completionInfo = getJsxClosingTagCompletion(location, sourceFile);
504            if (completionInfo) {
505                return completionInfo;
506            }
507        }
508
509        const entries = createSortedArray<CompletionEntry>();
510        const isChecked = isCheckedFile(sourceFile, compilerOptions);
511        if (isChecked && !isNewIdentifierLocation && (!symbols || symbols.length === 0) && keywordFilters === KeywordCompletionFilters.None) {
512            return undefined;
513        }
514        const uniqueNames = getCompletionEntriesFromSymbols(
515            symbols,
516            entries,
517            /*replacementToken*/ undefined,
518            contextToken,
519            location,
520            sourceFile,
521            host,
522            program,
523            getEmitScriptTarget(compilerOptions),
524            log,
525            completionKind,
526            preferences,
527            compilerOptions,
528            formatContext,
529            isTypeOnlyLocation,
530            propertyAccessToConvert,
531            isJsxIdentifierExpected,
532            isJsxInitializer,
533            importStatementCompletion,
534            recommendedCompletion,
535            symbolToOriginInfoMap,
536            symbolToSortTextMap,
537            isJsxIdentifierExpected,
538            isRightOfOpenTag,
539        );
540
541        if (keywordFilters !== KeywordCompletionFilters.None) {
542            for (const keywordEntry of getKeywordCompletions(keywordFilters, !insideJsDocTagTypeExpression && isSourceFileJS(sourceFile))) {
543                if (isTypeOnlyLocation && isTypeKeyword(stringToToken(keywordEntry.name)!) || !uniqueNames.has(keywordEntry.name)) {
544                    uniqueNames.add(keywordEntry.name);
545                    insertSorted(entries, keywordEntry, compareCompletionEntries, /*allowDuplicates*/ true);
546                }
547            }
548        }
549
550        for (const keywordEntry of getContextualKeywords(contextToken, position)) {
551            if (!uniqueNames.has(keywordEntry.name)) {
552                uniqueNames.add(keywordEntry.name);
553                insertSorted(entries, keywordEntry, compareCompletionEntries, /*allowDuplicates*/ true);
554            }
555        }
556
557        for (const literal of literals) {
558            const literalEntry = createCompletionEntryForLiteral(sourceFile, preferences, literal);
559            uniqueNames.add(literalEntry.name);
560            insertSorted(entries, literalEntry, compareCompletionEntries, /*allowDuplicates*/ true);
561        }
562
563        if (!isChecked) {
564            getJSCompletionEntries(sourceFile, location.pos, uniqueNames, getEmitScriptTarget(compilerOptions), entries);
565        }
566
567        return {
568            flags: completionData.flags,
569            isGlobalCompletion: isInSnippetScope,
570            isIncomplete: preferences.allowIncompleteCompletions && hasUnresolvedAutoImports ? true : undefined,
571            isMemberCompletion: isMemberCompletionKind(completionKind),
572            isNewIdentifierLocation,
573            optionalReplacementSpan: getOptionalReplacementSpan(location),
574            entries,
575        };
576    }
577
578    function isCheckedFile(sourceFile: SourceFile, compilerOptions: CompilerOptions): boolean {
579        return !isSourceFileJS(sourceFile) || !!isCheckJsEnabledForFile(sourceFile, compilerOptions);
580    }
581
582    function isMemberCompletionKind(kind: CompletionKind): boolean {
583        switch (kind) {
584            case CompletionKind.ObjectPropertyDeclaration:
585            case CompletionKind.MemberLike:
586            case CompletionKind.PropertyAccess:
587                return true;
588            default:
589                return false;
590        }
591    }
592
593    function getJsxClosingTagCompletion(location: Node | undefined, sourceFile: SourceFile): CompletionInfo | undefined {
594        // We wanna walk up the tree till we find a JSX closing element
595        const jsxClosingElement = findAncestor(location, node => {
596            switch (node.kind) {
597                case SyntaxKind.JsxClosingElement:
598                    return true;
599                case SyntaxKind.SlashToken:
600                case SyntaxKind.GreaterThanToken:
601                case SyntaxKind.Identifier:
602                case SyntaxKind.PropertyAccessExpression:
603                    return false;
604                default:
605                    return "quit";
606            }
607        }) as JsxClosingElement | undefined;
608
609        if (jsxClosingElement) {
610            // In the TypeScript JSX element, if such element is not defined. When users query for completion at closing tag,
611            // instead of simply giving unknown value, the completion will return the tag-name of an associated opening-element.
612            // For example:
613            //     var x = <div> </ /*1*/
614            // The completion list at "1" will contain "div>" with type any
615            // And at `<div> </ /*1*/ >` (with a closing `>`), the completion list will contain "div".
616            // And at property access expressions `<MainComponent.Child> </MainComponent. /*1*/ >` the completion will
617            // return full closing tag with an optional replacement span
618            // For example:
619            //     var x = <MainComponent.Child> </     MainComponent /*1*/  >
620            //     var y = <MainComponent.Child> </   /*2*/   MainComponent >
621            // the completion list at "1" and "2" will contain "MainComponent.Child" with a replacement span of closing tag name
622            const hasClosingAngleBracket = !!findChildOfKind(jsxClosingElement, SyntaxKind.GreaterThanToken, sourceFile);
623            const tagName = jsxClosingElement.parent.openingElement.tagName;
624            const closingTag = tagName.getText(sourceFile);
625            const fullClosingTag = closingTag + (hasClosingAngleBracket ? "" : ">");
626            const replacementSpan = createTextSpanFromNode(jsxClosingElement.tagName);
627
628            const entry: CompletionEntry = {
629                name: fullClosingTag,
630                kind: ScriptElementKind.classElement,
631                kindModifiers: undefined,
632                sortText: SortText.LocationPriority,
633            };
634            return { isGlobalCompletion: false, isMemberCompletion: true, isNewIdentifierLocation: false, optionalReplacementSpan: replacementSpan, entries: [entry] };
635        }
636        return;
637    }
638
639    function getJSCompletionEntries(
640        sourceFile: SourceFile,
641        position: number,
642        uniqueNames: UniqueNameSet,
643        target: ScriptTarget,
644        entries: SortedArray<CompletionEntry>): void {
645        getNameTable(sourceFile).forEach((pos, name) => {
646            // Skip identifiers produced only from the current location
647            if (pos === position) {
648                return;
649            }
650            const realName = unescapeLeadingUnderscores(name);
651            if (!uniqueNames.has(realName) && isIdentifierText(realName, target)) {
652                uniqueNames.add(realName);
653                insertSorted(entries, {
654                    name: realName,
655                    kind: ScriptElementKind.warning,
656                    kindModifiers: "",
657                    sortText: SortText.JavascriptIdentifiers,
658                    isFromUncheckedFile: true
659                }, compareCompletionEntries);
660            }
661        });
662    }
663
664    function completionNameForLiteral(sourceFile: SourceFile, preferences: UserPreferences, literal: string | number | PseudoBigInt): string {
665        return typeof literal === "object" ? pseudoBigIntToString(literal) + "n" :
666            isString(literal) ? quote(sourceFile, preferences, literal) : JSON.stringify(literal);
667    }
668
669    function createCompletionEntryForLiteral(sourceFile: SourceFile, preferences: UserPreferences, literal: string | number | PseudoBigInt): CompletionEntry {
670        return { name: completionNameForLiteral(sourceFile, preferences, literal), kind: ScriptElementKind.string, kindModifiers: ScriptElementKindModifier.none, sortText: SortText.LocationPriority };
671    }
672
673    function createCompletionEntry(
674        symbol: Symbol,
675        sortText: SortText,
676        replacementToken: Node | undefined,
677        contextToken: Node | undefined,
678        location: Node,
679        sourceFile: SourceFile,
680        host: LanguageServiceHost,
681        program: Program,
682        name: string,
683        needsConvertPropertyAccess: boolean,
684        origin: SymbolOriginInfo | undefined,
685        recommendedCompletion: Symbol | undefined,
686        propertyAccessToConvert: PropertyAccessExpression | undefined,
687        isJsxInitializer: IsJsxInitializer | undefined,
688        importStatementCompletion: ImportStatementCompletionInfo | undefined,
689        useSemicolons: boolean,
690        options: CompilerOptions,
691        preferences: UserPreferences,
692        completionKind: CompletionKind,
693        formatContext: formatting.FormatContext | undefined,
694        isJsxIdentifierExpected: boolean | undefined,
695        isRightOfOpenTag: boolean | undefined,
696    ): CompletionEntry | undefined {
697        let insertText: string | undefined;
698        let replacementSpan = getReplacementSpanForContextToken(replacementToken);
699        let data: CompletionEntryData | undefined;
700        let isSnippet: true | undefined;
701        let source = getSourceFromOrigin(origin);
702        let sourceDisplay;
703        let hasAction;
704        let labelDetails;
705
706        const typeChecker = program.getTypeChecker();
707        const insertQuestionDot = origin && originIsNullableMember(origin);
708        const useBraces = origin && originIsSymbolMember(origin) || needsConvertPropertyAccess;
709        if (origin && originIsThisType(origin)) {
710            insertText = needsConvertPropertyAccess
711                ? `this${insertQuestionDot ? "?." : ""}[${quotePropertyName(sourceFile, preferences, name)}]`
712                : `this${insertQuestionDot ? "?." : "."}${name}`;
713        }
714        // We should only have needsConvertPropertyAccess if there's a property access to convert. But see #21790.
715        // 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.
716        else if ((useBraces || insertQuestionDot) && propertyAccessToConvert) {
717            insertText = useBraces ? needsConvertPropertyAccess ? `[${quotePropertyName(sourceFile, preferences, name)}]` : `[${name}]` : name;
718            if (insertQuestionDot || propertyAccessToConvert.questionDotToken) {
719                insertText = `?.${insertText}`;
720            }
721
722            const dot = findChildOfKind(propertyAccessToConvert, SyntaxKind.DotToken, sourceFile) ||
723                findChildOfKind(propertyAccessToConvert, SyntaxKind.QuestionDotToken, sourceFile);
724            if (!dot) {
725                return undefined;
726            }
727            // If the text after the '.' starts with this name, write over it. Else, add new text.
728            const end = startsWith(name, propertyAccessToConvert.name.text) ? propertyAccessToConvert.name.end : dot.end;
729            replacementSpan = createTextSpanFromBounds(dot.getStart(sourceFile), end);
730        }
731
732        if (isJsxInitializer) {
733            if (insertText === undefined) insertText = name;
734            insertText = `{${insertText}}`;
735            if (typeof isJsxInitializer !== "boolean") {
736                replacementSpan = createTextSpanFromNode(isJsxInitializer, sourceFile);
737            }
738        }
739        if (origin && originIsPromise(origin) && propertyAccessToConvert) {
740            if (insertText === undefined) insertText = name;
741            const precedingToken = findPrecedingToken(propertyAccessToConvert.pos, sourceFile);
742            let awaitText = "";
743            if (precedingToken && positionIsASICandidate(precedingToken.end, precedingToken.parent, sourceFile)) {
744                awaitText = ";";
745            }
746
747            awaitText += `(await ${propertyAccessToConvert.expression.getText()})`;
748            insertText = needsConvertPropertyAccess ? `${awaitText}${insertText}` : `${awaitText}${insertQuestionDot ? "?." : "."}${insertText}`;
749            replacementSpan = createTextSpanFromBounds(propertyAccessToConvert.getStart(sourceFile), propertyAccessToConvert.end);
750        }
751
752        if (originIsResolvedExport(origin)) {
753            sourceDisplay = [textPart(origin.moduleSpecifier)];
754            if (importStatementCompletion) {
755                ({ insertText, replacementSpan } = getInsertTextAndReplacementSpanForImportCompletion(name, importStatementCompletion, origin, useSemicolons, sourceFile, options, preferences));
756                isSnippet = preferences.includeCompletionsWithSnippetText ? true : undefined;
757            }
758        }
759
760        if (origin?.kind === SymbolOriginInfoKind.TypeOnlyAlias) {
761            hasAction = true;
762        }
763
764        if (preferences.includeCompletionsWithClassMemberSnippets &&
765            preferences.includeCompletionsWithInsertText &&
766            completionKind === CompletionKind.MemberLike &&
767            isClassLikeMemberCompletion(symbol, location, sourceFile)) {
768            let importAdder;
769            ({ insertText, isSnippet, importAdder, replacementSpan } = getEntryForMemberCompletion(host, program, options, preferences, name, symbol, location, contextToken, formatContext));
770            sortText = SortText.ClassMemberSnippets; // sortText has to be lower priority than the sortText for keywords. See #47852.
771            if (importAdder?.hasFixes()) {
772                hasAction = true;
773                source = CompletionSource.ClassMemberSnippet;
774            }
775        }
776
777        if (origin && originIsObjectLiteralMethod(origin)) {
778            ({ insertText, isSnippet, labelDetails } = origin);
779            if (!preferences.useLabelDetailsInCompletionEntries) {
780                name = name + labelDetails.detail;
781                labelDetails = undefined;
782            }
783            source = CompletionSource.ObjectLiteralMethodSnippet;
784            sortText = SortText.SortBelow(sortText);
785        }
786
787        if (isJsxIdentifierExpected && !isRightOfOpenTag && preferences.includeCompletionsWithSnippetText && preferences.jsxAttributeCompletionStyle && preferences.jsxAttributeCompletionStyle !== "none") {
788            let useBraces = preferences.jsxAttributeCompletionStyle === "braces";
789            const type = typeChecker.getTypeOfSymbolAtLocation(symbol, location);
790
791            // If is boolean like or undefined, don't return a snippet we want just to return the completion.
792            if (preferences.jsxAttributeCompletionStyle === "auto"
793                && !(type.flags & TypeFlags.BooleanLike)
794                && !(type.flags & TypeFlags.Union && find((type as UnionType).types, type => !!(type.flags & TypeFlags.BooleanLike)))
795            ) {
796                if (type.flags & TypeFlags.StringLike || (type.flags & TypeFlags.Union && every((type as UnionType).types, type => !!(type.flags & (TypeFlags.StringLike | TypeFlags.Undefined))))) {
797                    // If is string like or undefined use quotes
798                    insertText = `${escapeSnippetText(name)}=${quote(sourceFile, preferences, "$1")}`;
799                    isSnippet = true;
800                }
801                else {
802                    // Use braces for everything else
803                    useBraces = true;
804                }
805            }
806
807            if (useBraces) {
808                insertText = `${escapeSnippetText(name)}={$1}`;
809                isSnippet = true;
810            }
811        }
812
813        if (insertText !== undefined && !preferences.includeCompletionsWithInsertText) {
814            return undefined;
815        }
816
817        if (originIsExport(origin) || originIsResolvedExport(origin)) {
818            data = originToCompletionEntryData(origin);
819            hasAction = !importStatementCompletion;
820        }
821
822        // TODO(drosen): Right now we just permit *all* semantic meanings when calling
823        // 'getSymbolKind' which is permissible given that it is backwards compatible; but
824        // really we should consider passing the meaning for the node so that we don't report
825        // that a suggestion for a value is an interface.  We COULD also just do what
826        // 'getSymbolModifiers' does, which is to use the first declaration.
827
828        // Use a 'sortText' of 0' so that all symbol completion entries come before any other
829        // entries (like JavaScript identifier entries).
830        return {
831            name,
832            kind: SymbolDisplay.getSymbolKind(typeChecker, symbol, location),
833            kindModifiers: SymbolDisplay.getSymbolModifiers(typeChecker, symbol),
834            sortText,
835            source,
836            hasAction: hasAction ? true : undefined,
837            isRecommended: isRecommendedCompletionMatch(symbol, recommendedCompletion, typeChecker) || undefined,
838            insertText,
839            replacementSpan,
840            sourceDisplay,
841            labelDetails,
842            isSnippet,
843            isPackageJsonImport: originIsPackageJsonImport(origin) || undefined,
844            isImportStatementCompletion: !!importStatementCompletion || undefined,
845            data,
846        };
847    }
848
849    function isClassLikeMemberCompletion(symbol: Symbol, location: Node, sourceFile: SourceFile): boolean {
850        // TODO: support JS files.
851        if (isInJSFile(location)) {
852            return false;
853        }
854
855        // Completion symbol must be for a class member.
856        const memberFlags =
857            SymbolFlags.ClassMember
858            & SymbolFlags.EnumMemberExcludes;
859        /* In
860        `class C {
861            |
862        }`
863        `location` is a class-like declaration.
864        In
865        `class C {
866            m|
867        }`
868        `location` is an identifier,
869        `location.parent` is a class element declaration,
870        and `location.parent.parent` is a class-like declaration.
871        In
872        `abstract class C {
873            abstract
874            abstract m|
875        }`
876        `location` is a syntax list (with modifiers as children),
877        and `location.parent` is a class-like declaration.
878        */
879        return !!(symbol.flags & memberFlags) &&
880            (
881                isClassLike(location) ||
882                (
883                    location.parent &&
884                    location.parent.parent &&
885                    isClassElement(location.parent) &&
886                    location === location.parent.name &&
887                    location.parent.getLastToken(sourceFile) === location.parent.name &&
888                    isClassLike(location.parent.parent)
889                ) ||
890                (
891                    location.parent &&
892                    isSyntaxList(location) &&
893                    isClassLike(location.parent)
894                )
895            );
896    }
897
898    function getEntryForMemberCompletion(
899        host: LanguageServiceHost,
900        program: Program,
901        options: CompilerOptions,
902        preferences: UserPreferences,
903        name: string,
904        symbol: Symbol,
905        location: Node,
906        contextToken: Node | undefined,
907        formatContext: formatting.FormatContext | undefined,
908    ): { insertText: string, isSnippet?: true, importAdder?: codefix.ImportAdder, replacementSpan?: TextSpan } {
909        const classLikeDeclaration = findAncestor(location, isClassLike);
910        if (!classLikeDeclaration) {
911            return { insertText: name };
912        }
913
914        let isSnippet: true | undefined;
915        let replacementSpan: TextSpan | undefined;
916        let insertText: string = name;
917
918        const checker = program.getTypeChecker();
919        const sourceFile = location.getSourceFile();
920        const printer = createSnippetPrinter({
921            removeComments: true,
922            module: options.module,
923            target: options.target,
924            omitTrailingSemicolon: false,
925            newLine: getNewLineKind(getNewLineCharacter(options, maybeBind(host, host.getNewLine))),
926        });
927        const importAdder = codefix.createImportAdder(sourceFile, program, preferences, host);
928
929        // Create empty body for possible method implementation.
930        let body;
931        if (preferences.includeCompletionsWithSnippetText) {
932            isSnippet = true;
933            // We are adding a tabstop (i.e. `$0`) in the body of the suggested member,
934            // if it has one, so that the cursor ends up in the body once the completion is inserted.
935            // Note: this assumes we won't have more than one body in the completion nodes, which should be the case.
936            const emptyStmt = factory.createEmptyStatement();
937            body = factory.createBlock([emptyStmt], /* multiline */ true);
938            setSnippetElement(emptyStmt, { kind: SnippetKind.TabStop, order: 0 });
939        }
940        else {
941            body = factory.createBlock([], /* multiline */ true);
942        }
943
944        let modifiers = ModifierFlags.None;
945        // Whether the suggested member should be abstract.
946        // e.g. in `abstract class C { abstract | }`, we should offer abstract method signatures at position `|`.
947        const { modifiers: presentModifiers, span: modifiersSpan } = getPresentModifiers(contextToken);
948        const isAbstract = !!(presentModifiers & ModifierFlags.Abstract);
949        const completionNodes: Node[] = [];
950        codefix.addNewNodeForMemberSymbol(
951            symbol,
952            classLikeDeclaration,
953            sourceFile,
954            { program, host },
955            preferences,
956            importAdder,
957            // `addNewNodeForMemberSymbol` calls this callback function for each new member node
958            // it adds for the given member symbol.
959            // We store these member nodes in the `completionNodes` array.
960            // Note: there might be:
961            //  - No nodes if `addNewNodeForMemberSymbol` cannot figure out a node for the member;
962            //  - One node;
963            //  - More than one node if the member is overloaded (e.g. a method with overload signatures).
964            node => {
965                let requiredModifiers = ModifierFlags.None;
966                if (isAbstract) {
967                    requiredModifiers |= ModifierFlags.Abstract;
968                }
969                if (isClassElement(node)
970                    && checker.getMemberOverrideModifierStatus(classLikeDeclaration, node) === MemberOverrideStatus.NeedsOverride) {
971                    requiredModifiers |= ModifierFlags.Override;
972                }
973
974                if (!completionNodes.length) {
975                    // Keep track of added missing required modifiers and modifiers already present.
976                    // This is needed when we have overloaded signatures,
977                    // so this callback will be called for multiple nodes/signatures,
978                    // and we need to make sure the modifiers are uniform for all nodes/signatures.
979                    modifiers = node.modifierFlagsCache | requiredModifiers | presentModifiers;
980                }
981                node = factory.updateModifiers(node, modifiers);
982                completionNodes.push(node);
983            },
984            body,
985            codefix.PreserveOptionalFlags.Property,
986            isAbstract);
987
988        if (completionNodes.length) {
989            const format = ListFormat.MultiLine | ListFormat.NoTrailingNewLine;
990            replacementSpan = modifiersSpan;
991             // If we have access to formatting settings, we print the nodes using the emitter,
992             // and then format the printed text.
993            if (formatContext) {
994                insertText = printer.printAndFormatSnippetList(
995                    format,
996                    factory.createNodeArray(completionNodes),
997                    sourceFile,
998                    formatContext);
999            }
1000            else { // Otherwise, just use emitter to print the new nodes.
1001                insertText = printer.printSnippetList(
1002                    format,
1003                    factory.createNodeArray(completionNodes),
1004                    sourceFile);
1005            }
1006        }
1007
1008        return { insertText, isSnippet, importAdder, replacementSpan };
1009    }
1010
1011    function getPresentModifiers(contextToken: Node | undefined): { modifiers: ModifierFlags, span?: TextSpan } {
1012        if (!contextToken) {
1013            return { modifiers: ModifierFlags.None };
1014        }
1015        let modifiers = ModifierFlags.None;
1016        let span;
1017        let contextMod;
1018        /*
1019        Cases supported:
1020        In
1021        `class C {
1022            public abstract |
1023        }`
1024        `contextToken` is ``abstract`` (as an identifier),
1025        `contextToken.parent` is property declaration,
1026        `location` is class declaration ``class C { ... }``.
1027        In
1028        `class C {
1029            protected override m|
1030        }`
1031            `contextToken` is ``override`` (as a keyword),
1032        `contextToken.parent` is property declaration,
1033        `location` is identifier ``m``,
1034        `location.parent` is property declaration ``protected override m``,
1035        `location.parent.parent` is class declaration ``class C { ... }``.
1036        */
1037        if (contextMod = isModifierLike(contextToken)) {
1038            modifiers |= modifierToFlag(contextMod);
1039            span = createTextSpanFromNode(contextToken);
1040        }
1041        if (isPropertyDeclaration(contextToken.parent)) {
1042            modifiers |= modifiersToFlags(contextToken.parent.modifiers) & ModifierFlags.Modifier;
1043            span = createTextSpanFromNode(contextToken.parent);
1044        }
1045        return { modifiers, span };
1046    }
1047
1048    function isModifierLike(node: Node): ModifierSyntaxKind | undefined {
1049        if (isModifier(node)) {
1050            return node.kind;
1051        }
1052        if (isIdentifier(node) && node.originalKeywordKind && isModifierKind(node.originalKeywordKind)) {
1053            return node.originalKeywordKind;
1054        }
1055        return undefined;
1056    }
1057
1058    function getEntryForObjectLiteralMethodCompletion(
1059        symbol: Symbol,
1060        name: string,
1061        enclosingDeclaration: ObjectLiteralExpression,
1062        program: Program,
1063        host: LanguageServiceHost,
1064        options: CompilerOptions,
1065        preferences: UserPreferences,
1066        formatContext: formatting.FormatContext | undefined,
1067    ): { insertText: string, isSnippet?: true, labelDetails: CompletionEntryLabelDetails } | undefined {
1068        const isSnippet = preferences.includeCompletionsWithSnippetText || undefined;
1069        let insertText: string = name;
1070
1071        const sourceFile = enclosingDeclaration.getSourceFile();
1072
1073        const method = createObjectLiteralMethod(symbol, enclosingDeclaration, sourceFile, program, host, preferences);
1074        if (!method) {
1075            return undefined;
1076        }
1077
1078        const printer = createSnippetPrinter({
1079            removeComments: true,
1080            module: options.module,
1081            target: options.target,
1082            omitTrailingSemicolon: false,
1083            newLine: getNewLineKind(getNewLineCharacter(options, maybeBind(host, host.getNewLine))),
1084        });
1085        if (formatContext) {
1086            insertText = printer.printAndFormatSnippetList(ListFormat.CommaDelimited | ListFormat.AllowTrailingComma, factory.createNodeArray([method], /*hasTrailingComma*/ true), sourceFile, formatContext);
1087        }
1088        else {
1089            insertText = printer.printSnippetList(ListFormat.CommaDelimited | ListFormat.AllowTrailingComma, factory.createNodeArray([method], /*hasTrailingComma*/ true), sourceFile);
1090        }
1091
1092        const signaturePrinter = createPrinter({
1093            removeComments: true,
1094            module: options.module,
1095            target: options.target,
1096            omitTrailingSemicolon: true,
1097        });
1098        // The `labelDetails.detail` will be displayed right beside the method name,
1099        // so we drop the name (and modifiers) from the signature.
1100        const methodSignature = factory.createMethodSignature(
1101            /*modifiers*/ undefined,
1102            /*name*/ "",
1103            method.questionToken,
1104            method.typeParameters,
1105            method.parameters,
1106            method.type);
1107        const labelDetails = { detail: signaturePrinter.printNode(EmitHint.Unspecified, methodSignature, sourceFile) };
1108
1109        return { isSnippet, insertText, labelDetails };
1110
1111    }
1112
1113    function createObjectLiteralMethod(
1114        symbol: Symbol,
1115        enclosingDeclaration: ObjectLiteralExpression,
1116        sourceFile: SourceFile,
1117        program: Program,
1118        host: LanguageServiceHost,
1119        preferences: UserPreferences,
1120    ): MethodDeclaration | undefined {
1121        const declarations = symbol.getDeclarations();
1122        if (!(declarations && declarations.length)) {
1123            return undefined;
1124        }
1125        const checker = program.getTypeChecker();
1126        const declaration = declarations[0];
1127        const name = getSynthesizedDeepClone(getNameOfDeclaration(declaration), /*includeTrivia*/ false) as PropertyName;
1128        const type = checker.getWidenedType(checker.getTypeOfSymbolAtLocation(symbol, enclosingDeclaration));
1129        const quotePreference = getQuotePreference(sourceFile, preferences);
1130        const builderFlags = NodeBuilderFlags.OmitThisParameter | (quotePreference === QuotePreference.Single ? NodeBuilderFlags.UseSingleQuotesForStringLiteralType : NodeBuilderFlags.None);
1131
1132        switch (declaration.kind) {
1133            case SyntaxKind.PropertySignature:
1134            case SyntaxKind.PropertyDeclaration:
1135            case SyntaxKind.MethodSignature:
1136            case SyntaxKind.MethodDeclaration: {
1137                let effectiveType = type.flags & TypeFlags.Union && (type as UnionType).types.length < 10
1138                    ? checker.getUnionType((type as UnionType).types, UnionReduction.Subtype)
1139                    : type;
1140                if (effectiveType.flags & TypeFlags.Union) {
1141                    // Only offer the completion if there's a single function type component.
1142                    const functionTypes = filter((effectiveType as UnionType).types, type => checker.getSignaturesOfType(type, SignatureKind.Call).length > 0);
1143                    if (functionTypes.length === 1) {
1144                        effectiveType = functionTypes[0];
1145                    }
1146                    else {
1147                        return undefined;
1148                    }
1149                }
1150                const signatures = checker.getSignaturesOfType(effectiveType, SignatureKind.Call);
1151                if (signatures.length !== 1) {
1152                    // We don't support overloads in object literals.
1153                    return undefined;
1154                }
1155                const typeNode = checker.typeToTypeNode(effectiveType, enclosingDeclaration, builderFlags, codefix.getNoopSymbolTrackerWithResolver({ program, host }));
1156                if (!typeNode || !isFunctionTypeNode(typeNode)) {
1157                    return undefined;
1158                }
1159
1160                let body;
1161                if (preferences.includeCompletionsWithSnippetText) {
1162                    const emptyStmt = factory.createEmptyStatement();
1163                    body = factory.createBlock([emptyStmt], /* multiline */ true);
1164                    setSnippetElement(emptyStmt, { kind: SnippetKind.TabStop, order: 0 });
1165                }
1166                else {
1167                    body = factory.createBlock([], /* multiline */ true);
1168                }
1169
1170                const parameters = typeNode.parameters.map(typedParam =>
1171                    factory.createParameterDeclaration(
1172                        /*modifiers*/ undefined,
1173                        typedParam.dotDotDotToken,
1174                        typedParam.name,
1175                        /*questionToken*/ undefined,
1176                        /*type*/ undefined,
1177                        typedParam.initializer,
1178                    ));
1179                return factory.createMethodDeclaration(
1180                    /*modifiers*/ undefined,
1181                    /*asteriskToken*/ undefined,
1182                    name,
1183                    /*questionToken*/ undefined,
1184                    /*typeParameters*/ undefined,
1185                    parameters,
1186                    /*type*/ undefined,
1187                    body);
1188                }
1189            default:
1190                return undefined;
1191        }
1192    }
1193
1194    function createSnippetPrinter(
1195        printerOptions: PrinterOptions,
1196    ) {
1197        let escapes: TextChange[] | undefined;
1198        const baseWriter = textChanges.createWriter(getNewLineCharacter(printerOptions));
1199        const printer = createPrinter(printerOptions, baseWriter);
1200        const writer: EmitTextWriter = {
1201            ...baseWriter,
1202            write: s => escapingWrite(s, () => baseWriter.write(s)),
1203            nonEscapingWrite: baseWriter.write,
1204            writeLiteral: s => escapingWrite(s, () => baseWriter.writeLiteral(s)),
1205            writeStringLiteral: s => escapingWrite(s, () => baseWriter.writeStringLiteral(s)),
1206            writeSymbol: (s, symbol) => escapingWrite(s, () => baseWriter.writeSymbol(s, symbol)),
1207            writeParameter: s => escapingWrite(s, () => baseWriter.writeParameter(s)),
1208            writeComment: s => escapingWrite(s, () => baseWriter.writeComment(s)),
1209            writeProperty: s => escapingWrite(s, () => baseWriter.writeProperty(s)),
1210        };
1211
1212        return {
1213            printSnippetList,
1214            printAndFormatSnippetList,
1215        };
1216
1217        // The formatter/scanner will have issues with snippet-escaped text,
1218        // so instead of writing the escaped text directly to the writer,
1219        // generate a set of changes that can be applied to the unescaped text
1220        // to escape it post-formatting.
1221        function escapingWrite(s: string, write: () => void) {
1222            const escaped = escapeSnippetText(s);
1223            if (escaped !== s) {
1224                const start = baseWriter.getTextPos();
1225                write();
1226                const end = baseWriter.getTextPos();
1227                escapes = append(escapes ||= [], { newText: escaped, span: { start, length: end - start } });
1228            }
1229            else {
1230                write();
1231            }
1232        }
1233
1234        /* Snippet-escaping version of `printer.printList`. */
1235        function printSnippetList(
1236            format: ListFormat,
1237            list: NodeArray<Node>,
1238            sourceFile: SourceFile | undefined,
1239        ): string {
1240            const unescaped = printUnescapedSnippetList(format, list, sourceFile);
1241            return escapes ? textChanges.applyChanges(unescaped, escapes) : unescaped;
1242        }
1243
1244        function printUnescapedSnippetList(
1245            format: ListFormat,
1246            list: NodeArray<Node>,
1247            sourceFile: SourceFile | undefined,
1248        ): string {
1249            escapes = undefined;
1250            writer.clear();
1251            printer.writeList(format, list, sourceFile, writer);
1252            return writer.getText();
1253        }
1254
1255        function printAndFormatSnippetList(
1256            format: ListFormat,
1257            list: NodeArray<Node>,
1258            sourceFile: SourceFile,
1259            formatContext: formatting.FormatContext,
1260        ): string {
1261            const syntheticFile = {
1262                text: printUnescapedSnippetList(
1263                    format,
1264                    list,
1265                    sourceFile),
1266                getLineAndCharacterOfPosition(pos: number) {
1267                    return getLineAndCharacterOfPosition(this, pos);
1268                },
1269            };
1270
1271            const formatOptions = getFormatCodeSettingsForWriting(formatContext, sourceFile);
1272            const changes = flatMap(list, node => {
1273                const nodeWithPos = textChanges.assignPositionsToNode(node);
1274                return formatting.formatNodeGivenIndentation(
1275                    nodeWithPos,
1276                    syntheticFile,
1277                    sourceFile.languageVariant,
1278                    /* indentation */ 0,
1279                    /* delta */ 0,
1280                    { ...formatContext, options: formatOptions });
1281            });
1282
1283            const allChanges = escapes
1284                ? stableSort(concatenate(changes, escapes), (a, b) => compareTextSpans(a.span, b.span))
1285                : changes;
1286            return textChanges.applyChanges(syntheticFile.text, allChanges);
1287        }
1288    }
1289
1290    function originToCompletionEntryData(origin: SymbolOriginInfoExport | SymbolOriginInfoResolvedExport): CompletionEntryData | undefined {
1291        const ambientModuleName = origin.fileName ? undefined : stripQuotes(origin.moduleSymbol.name);
1292        const isPackageJsonImport = origin.isFromPackageJson ? true : undefined;
1293        if (originIsResolvedExport(origin)) {
1294            const resolvedData: CompletionEntryDataResolved = {
1295                exportName: origin.exportName,
1296                moduleSpecifier: origin.moduleSpecifier,
1297                ambientModuleName,
1298                fileName: origin.fileName,
1299                isPackageJsonImport,
1300            };
1301            return resolvedData;
1302        }
1303        const unresolvedData: CompletionEntryDataUnresolved = {
1304            exportName: origin.exportName,
1305            exportMapKey: origin.exportMapKey,
1306            fileName: origin.fileName,
1307            ambientModuleName: origin.fileName ? undefined : stripQuotes(origin.moduleSymbol.name),
1308            isPackageJsonImport: origin.isFromPackageJson ? true : undefined,
1309        };
1310        return unresolvedData;
1311    }
1312
1313    function completionEntryDataToSymbolOriginInfo(data: CompletionEntryData, completionName: string, moduleSymbol: Symbol): SymbolOriginInfoExport | SymbolOriginInfoResolvedExport {
1314        const isDefaultExport = data.exportName === InternalSymbolName.Default;
1315        const isFromPackageJson = !!data.isPackageJsonImport;
1316        if (completionEntryDataIsResolved(data)) {
1317            const resolvedOrigin: SymbolOriginInfoResolvedExport = {
1318                kind: SymbolOriginInfoKind.ResolvedExport,
1319                exportName: data.exportName,
1320                moduleSpecifier: data.moduleSpecifier,
1321                symbolName: completionName,
1322                fileName: data.fileName,
1323                moduleSymbol,
1324                isDefaultExport,
1325                isFromPackageJson,
1326            };
1327            return resolvedOrigin;
1328        }
1329        const unresolvedOrigin: SymbolOriginInfoExport = {
1330            kind: SymbolOriginInfoKind.Export,
1331            exportName: data.exportName,
1332            exportMapKey: data.exportMapKey,
1333            symbolName: completionName,
1334            fileName: data.fileName,
1335            moduleSymbol,
1336            isDefaultExport,
1337            isFromPackageJson,
1338        };
1339        return unresolvedOrigin;
1340    }
1341
1342    function getInsertTextAndReplacementSpanForImportCompletion(name: string, importStatementCompletion: ImportStatementCompletionInfo, origin: SymbolOriginInfoResolvedExport, useSemicolons: boolean, sourceFile: SourceFile, options: CompilerOptions, preferences: UserPreferences) {
1343        const replacementSpan = importStatementCompletion.replacementSpan;
1344        const quotedModuleSpecifier = quote(sourceFile, preferences, origin.moduleSpecifier);
1345        const exportKind =
1346            origin.isDefaultExport ? ExportKind.Default :
1347            origin.exportName === InternalSymbolName.ExportEquals ? ExportKind.ExportEquals :
1348            ExportKind.Named;
1349        const tabStop = preferences.includeCompletionsWithSnippetText ? "$1" : "";
1350        const importKind = codefix.getImportKind(sourceFile, exportKind, options, /*forceImportKeyword*/ true);
1351        const isImportSpecifierTypeOnly = importStatementCompletion.couldBeTypeOnlyImportSpecifier;
1352        const topLevelTypeOnlyText = importStatementCompletion.isTopLevelTypeOnly ? ` ${tokenToString(SyntaxKind.TypeKeyword)} ` : " ";
1353        const importSpecifierTypeOnlyText = isImportSpecifierTypeOnly ? `${tokenToString(SyntaxKind.TypeKeyword)} ` : "";
1354        const suffix = useSemicolons ? ";" : "";
1355        switch (importKind) {
1356            case ImportKind.CommonJS: return { replacementSpan, insertText: `import${topLevelTypeOnlyText}${escapeSnippetText(name)}${tabStop} = require(${quotedModuleSpecifier})${suffix}` };
1357            case ImportKind.Default: return { replacementSpan, insertText: `import${topLevelTypeOnlyText}${escapeSnippetText(name)}${tabStop} from ${quotedModuleSpecifier}${suffix}` };
1358            case ImportKind.Namespace: return { replacementSpan, insertText: `import${topLevelTypeOnlyText}* as ${escapeSnippetText(name)} from ${quotedModuleSpecifier}${suffix}` };
1359            case ImportKind.Named: return { replacementSpan, insertText: `import${topLevelTypeOnlyText}{ ${importSpecifierTypeOnlyText}${escapeSnippetText(name)}${tabStop} } from ${quotedModuleSpecifier}${suffix}` };
1360        }
1361    }
1362
1363    function quotePropertyName(sourceFile: SourceFile, preferences: UserPreferences, name: string,): string {
1364        if (/^\d+$/.test(name)) {
1365            return name;
1366        }
1367
1368        return quote(sourceFile, preferences, name);
1369    }
1370
1371    function isRecommendedCompletionMatch(localSymbol: Symbol, recommendedCompletion: Symbol | undefined, checker: TypeChecker): boolean {
1372        return localSymbol === recommendedCompletion ||
1373            !!(localSymbol.flags & SymbolFlags.ExportValue) && checker.getExportSymbolOfSymbol(localSymbol) === recommendedCompletion;
1374    }
1375
1376    function getSourceFromOrigin(origin: SymbolOriginInfo | undefined): string | undefined {
1377        if (originIsExport(origin)) {
1378            return stripQuotes(origin.moduleSymbol.name);
1379        }
1380        if (originIsResolvedExport(origin)) {
1381            return origin.moduleSpecifier;
1382        }
1383        if (origin?.kind === SymbolOriginInfoKind.ThisType) {
1384            return CompletionSource.ThisProperty;
1385        }
1386        if (origin?.kind === SymbolOriginInfoKind.TypeOnlyAlias) {
1387            return CompletionSource.TypeOnlyAlias;
1388        }
1389    }
1390
1391    export function getCompletionEntriesFromSymbols(
1392        symbols: readonly Symbol[],
1393        entries: SortedArray<CompletionEntry>,
1394        replacementToken: Node | undefined,
1395        contextToken: Node | undefined,
1396        location: Node,
1397        sourceFile: SourceFile,
1398        host: LanguageServiceHost,
1399        program: Program,
1400        target: ScriptTarget,
1401        log: Log,
1402        kind: CompletionKind,
1403        preferences: UserPreferences,
1404        compilerOptions: CompilerOptions,
1405        formatContext: formatting.FormatContext | undefined,
1406        isTypeOnlyLocation?: boolean,
1407        propertyAccessToConvert?: PropertyAccessExpression,
1408        jsxIdentifierExpected?: boolean,
1409        isJsxInitializer?: IsJsxInitializer,
1410        importStatementCompletion?: ImportStatementCompletionInfo,
1411        recommendedCompletion?: Symbol,
1412        symbolToOriginInfoMap?: SymbolOriginInfoMap,
1413        symbolToSortTextMap?: SymbolSortTextMap,
1414        isJsxIdentifierExpected?: boolean,
1415        isRightOfOpenTag?: boolean,
1416    ): UniqueNameSet {
1417        const start = timestamp();
1418        const variableDeclaration = getVariableDeclaration(location);
1419        const useSemicolons = probablyUsesSemicolons(sourceFile);
1420        const typeChecker = program.getTypeChecker();
1421        // Tracks unique names.
1422        // Value is set to false for global variables or completions from external module exports, because we can have multiple of those;
1423        // true otherwise. Based on the order we add things we will always see locals first, then globals, then module exports.
1424        // So adding a completion for a local will prevent us from adding completions for external module exports sharing the same name.
1425        const uniques = new Map<string, boolean>();
1426        for (let i = 0; i < symbols.length; i++) {
1427            const symbol = symbols[i];
1428            const origin = symbolToOriginInfoMap?.[i];
1429            const info = getCompletionEntryDisplayNameForSymbol(symbol, target, origin, kind, !!jsxIdentifierExpected);
1430            if (!info || (uniques.get(info.name) && (!origin || !originIsObjectLiteralMethod(origin))) || kind === CompletionKind.Global && symbolToSortTextMap && !shouldIncludeSymbol(symbol, symbolToSortTextMap)) {
1431                continue;
1432            }
1433
1434            const { name, needsConvertPropertyAccess } = info;
1435            const originalSortText = symbolToSortTextMap?.[getSymbolId(symbol)] ?? SortText.LocationPriority;
1436            const sortText = (isDeprecated(symbol, typeChecker) ? SortText.Deprecated(originalSortText) : originalSortText);
1437            const entry = createCompletionEntry(
1438                symbol,
1439                sortText,
1440                replacementToken,
1441                contextToken,
1442                location,
1443                sourceFile,
1444                host,
1445                program,
1446                name,
1447                needsConvertPropertyAccess,
1448                origin,
1449                recommendedCompletion,
1450                propertyAccessToConvert,
1451                isJsxInitializer,
1452                importStatementCompletion,
1453                useSemicolons,
1454                compilerOptions,
1455                preferences,
1456                kind,
1457                formatContext,
1458                isJsxIdentifierExpected,
1459                isRightOfOpenTag,
1460            );
1461            if (!entry) {
1462                continue;
1463            }
1464
1465            /** True for locals; false for globals, module exports from other files, `this.` completions. */
1466            const shouldShadowLaterSymbols = (!origin || originIsTypeOnlyAlias(origin)) && !(symbol.parent === undefined && !some(symbol.declarations, d => d.getSourceFile() === location.getSourceFile()));
1467            uniques.set(name, shouldShadowLaterSymbols);
1468            // add jsDoc info at interface getCompletionsAtPosition
1469            if (symbol.getJsDocTags().length > 0) {
1470                entry.jsDoc = symbol.getJsDocTags();
1471            }
1472            if (symbol.declarations && i < 50) {
1473                const symbolDisplayPartsDocumentationAndSymbolKind = SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker, symbol, sourceFile, location, location, SemanticMeaning.All);
1474                const container = getContaningConstructorDeclaration(symbol.valueDeclaration);
1475                const regex = /(Missing)/g;
1476                entry.displayParts = container && container.virtual ? symbolDisplayPartsDocumentationAndSymbolKind.displayParts.map(part => {
1477                    if (part.text.match(regex)) {
1478                        part.text = part.text.replace(regex, entry.name);
1479                    }
1480                    return part;
1481                }) : symbolDisplayPartsDocumentationAndSymbolKind.displayParts;
1482            }
1483            insertSorted(entries, entry, compareCompletionEntries, /*allowDuplicates*/ true);
1484        }
1485
1486        log("getCompletionsAtPosition: getCompletionEntriesFromSymbols: " + (timestamp() - start));
1487
1488        // Prevent consumers of this map from having to worry about
1489        // the boolean value. Externally, it should be seen as the
1490        // set of all names.
1491        return {
1492            has: name => uniques.has(name),
1493            add: name => uniques.set(name, true),
1494        };
1495
1496        function shouldIncludeSymbol(symbol: Symbol, symbolToSortTextMap: SymbolSortTextMap): boolean {
1497            let allFlags = symbol.flags;
1498            if (!isSourceFile(location)) {
1499                // export = /**/ here we want to get all meanings, so any symbol is ok
1500                if (isExportAssignment(location.parent)) {
1501                    return true;
1502                }
1503                // Filter out variables from their own initializers
1504                // `const a = /* no 'a' here */`
1505                if (variableDeclaration && symbol.valueDeclaration === variableDeclaration) {
1506                    return false;
1507                }
1508
1509                // External modules can have global export declarations that will be
1510                // available as global keywords in all scopes. But if the external module
1511                // already has an explicit export and user only wants to user explicit
1512                // module imports then the global keywords will be filtered out so auto
1513                // import suggestions will win in the completion
1514                const symbolOrigin = skipAlias(symbol, typeChecker);
1515                // We only want to filter out the global keywords
1516                // Auto Imports are not available for scripts so this conditional is always false
1517                if (!!sourceFile.externalModuleIndicator
1518                    && !compilerOptions.allowUmdGlobalAccess
1519                    && symbolToSortTextMap[getSymbolId(symbol)] === SortText.GlobalsOrKeywords
1520                    && (symbolToSortTextMap[getSymbolId(symbolOrigin)] === SortText.AutoImportSuggestions
1521                        || symbolToSortTextMap[getSymbolId(symbolOrigin)] === SortText.LocationPriority)) {
1522                    return false;
1523                }
1524
1525                allFlags |= getCombinedLocalAndExportSymbolFlags(symbolOrigin);
1526
1527                // import m = /**/ <-- It can only access namespace (if typing import = x. this would get member symbols and not namespace)
1528                if (isInRightSideOfInternalImportEqualsDeclaration(location)) {
1529                    return !!(allFlags & SymbolFlags.Namespace);
1530                }
1531
1532                if (isTypeOnlyLocation) {
1533                    // It's a type, but you can reach it by namespace.type as well
1534                    return symbolCanBeReferencedAtTypeLocation(symbol, typeChecker);
1535                }
1536            }
1537
1538            // expressions are value space (which includes the value namespaces)
1539            return !!(allFlags & SymbolFlags.Value);
1540        }
1541    }
1542
1543    function getLabelCompletionAtPosition(node: BreakOrContinueStatement): CompletionInfo | undefined {
1544        const entries = getLabelStatementCompletions(node);
1545        if (entries.length) {
1546            return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, entries };
1547        }
1548    }
1549
1550    function getLabelStatementCompletions(node: Node): CompletionEntry[] {
1551        const entries: CompletionEntry[] = [];
1552        const uniques = new Map<string, true>();
1553        let current = node;
1554
1555        while (current) {
1556            if (isFunctionLike(current)) {
1557                break;
1558            }
1559            if (isLabeledStatement(current)) {
1560                const name = current.label.text;
1561                if (!uniques.has(name)) {
1562                    uniques.set(name, true);
1563                    entries.push({
1564                        name,
1565                        kindModifiers: ScriptElementKindModifier.none,
1566                        kind: ScriptElementKind.label,
1567                        sortText: SortText.LocationPriority
1568                    });
1569                }
1570            }
1571            current = current.parent;
1572        }
1573        return entries;
1574    }
1575
1576    interface SymbolCompletion {
1577        type: "symbol";
1578        symbol: Symbol;
1579        location: Node;
1580        origin: SymbolOriginInfo | SymbolOriginInfoExport | SymbolOriginInfoResolvedExport | undefined;
1581        previousToken: Node | undefined;
1582        contextToken: Node | undefined;
1583        readonly isJsxInitializer: IsJsxInitializer;
1584        readonly isTypeOnlyLocation: boolean;
1585    }
1586    function getSymbolCompletionFromEntryId(
1587        program: Program,
1588        log: Log,
1589        sourceFile: SourceFile,
1590        position: number,
1591        entryId: CompletionEntryIdentifier,
1592        host: LanguageServiceHost,
1593        preferences: UserPreferences,
1594    ): SymbolCompletion | { type: "request", request: Request } | { type: "literal", literal: string | number | PseudoBigInt } | { type: "none" } {
1595        if (entryId.data) {
1596            const autoImport = getAutoImportSymbolFromCompletionEntryData(entryId.name, entryId.data, program, host);
1597            if (autoImport) {
1598                const { contextToken, previousToken } = getRelevantTokens(position, sourceFile);
1599                return {
1600                    type: "symbol",
1601                    symbol: autoImport.symbol,
1602                    location: getTouchingPropertyName(sourceFile, position),
1603                    previousToken,
1604                    contextToken,
1605                    isJsxInitializer: false,
1606                    isTypeOnlyLocation: false,
1607                    origin: autoImport.origin,
1608                };
1609            }
1610        }
1611
1612        const compilerOptions = program.getCompilerOptions();
1613        const completionData = getCompletionData(program, log, sourceFile, compilerOptions, position, { includeCompletionsForModuleExports: true, includeCompletionsWithInsertText: true }, entryId, host, /*formatContext*/ undefined);
1614        if (!completionData) {
1615            return { type: "none" };
1616        }
1617        if (completionData.kind !== CompletionDataKind.Data) {
1618            return { type: "request", request: completionData };
1619        }
1620
1621        const { symbols, literals, location, completionKind, symbolToOriginInfoMap, contextToken, previousToken, isJsxInitializer, isTypeOnlyLocation } = completionData;
1622
1623        const literal = find(literals, l => completionNameForLiteral(sourceFile, preferences, l) === entryId.name);
1624        if (literal !== undefined) return { type: "literal", literal };
1625
1626        // Find the symbol with the matching entry name.
1627        // We don't need to perform character checks here because we're only comparing the
1628        // name against 'entryName' (which is known to be good), not building a new
1629        // completion entry.
1630        return firstDefined(symbols, (symbol, index): SymbolCompletion | undefined => {
1631            const origin = symbolToOriginInfoMap[index];
1632            const info = getCompletionEntryDisplayNameForSymbol(symbol, getEmitScriptTarget(compilerOptions), origin, completionKind, completionData.isJsxIdentifierExpected);
1633            return info && info.name === entryId.name && (
1634                entryId.source === CompletionSource.ClassMemberSnippet && symbol.flags & SymbolFlags.ClassMember
1635                || entryId.source === CompletionSource.ObjectLiteralMethodSnippet && symbol.flags & (SymbolFlags.Property | SymbolFlags.Method)
1636                || getSourceFromOrigin(origin) === entryId.source)
1637                ? { type: "symbol" as const, symbol, location, origin, contextToken, previousToken, isJsxInitializer, isTypeOnlyLocation }
1638                : undefined;
1639        }) || { type: "none" };
1640    }
1641
1642    export interface CompletionEntryIdentifier {
1643        name: string;
1644        source?: string;
1645        data?: CompletionEntryData;
1646    }
1647
1648    export function getCompletionEntryDetails(
1649        program: Program,
1650        log: Log,
1651        sourceFile: SourceFile,
1652        position: number,
1653        entryId: CompletionEntryIdentifier,
1654        host: LanguageServiceHost,
1655        formatContext: formatting.FormatContext,
1656        preferences: UserPreferences,
1657        cancellationToken: CancellationToken,
1658    ): CompletionEntryDetails | undefined {
1659        const typeChecker = program.getTypeChecker();
1660        const compilerOptions = program.getCompilerOptions();
1661        const { name, source, data } = entryId;
1662
1663        const contextToken = findPrecedingToken(position, sourceFile);
1664        if (isInString(sourceFile, position, contextToken)) {
1665            return StringCompletions.getStringLiteralCompletionDetails(name, sourceFile, position, contextToken, typeChecker, compilerOptions, host, cancellationToken, preferences);
1666        }
1667
1668        // Compute all the completion symbols again.
1669        const symbolCompletion = getSymbolCompletionFromEntryId(program, log, sourceFile, position, entryId, host, preferences);
1670        switch (symbolCompletion.type) {
1671            case "request": {
1672                const { request } = symbolCompletion;
1673                switch (request.kind) {
1674                    case CompletionDataKind.JsDocTagName:
1675                        return JsDoc.getJSDocTagNameCompletionDetails(name);
1676                    case CompletionDataKind.JsDocTag:
1677                        return JsDoc.getJSDocTagCompletionDetails(name);
1678                    case CompletionDataKind.JsDocParameterName:
1679                        return JsDoc.getJSDocParameterNameCompletionDetails(name);
1680                    case CompletionDataKind.Keywords:
1681                        return some(request.keywordCompletions, c => c.name === name) ? createSimpleDetails(name, ScriptElementKind.keyword, SymbolDisplayPartKind.keyword) : undefined;
1682                    default:
1683                        return Debug.assertNever(request);
1684                }
1685            }
1686            case "symbol": {
1687                const { symbol, location, contextToken, origin, previousToken } = symbolCompletion;
1688                const { codeActions, sourceDisplay } = getCompletionEntryCodeActionsAndSourceDisplay(name, location, contextToken, origin, symbol, program, host, compilerOptions, sourceFile, position, previousToken, formatContext, preferences, data, source, cancellationToken);
1689                return createCompletionDetailsForSymbol(symbol, typeChecker, sourceFile, location, cancellationToken, codeActions, sourceDisplay); // TODO: GH#18217
1690            }
1691            case "literal": {
1692                const { literal } = symbolCompletion;
1693                return createSimpleDetails(completionNameForLiteral(sourceFile, preferences, literal), ScriptElementKind.string, typeof literal === "string" ? SymbolDisplayPartKind.stringLiteral : SymbolDisplayPartKind.numericLiteral);
1694            }
1695            case "none":
1696                // Didn't find a symbol with this name.  See if we can find a keyword instead.
1697                return allKeywordsCompletions().some(c => c.name === name) ? createSimpleDetails(name, ScriptElementKind.keyword, SymbolDisplayPartKind.keyword) : undefined;
1698            default:
1699                Debug.assertNever(symbolCompletion);
1700        }
1701    }
1702
1703    function createSimpleDetails(name: string, kind: ScriptElementKind, kind2: SymbolDisplayPartKind): CompletionEntryDetails {
1704        return createCompletionDetails(name, ScriptElementKindModifier.none, kind, [displayPart(name, kind2)]);
1705    }
1706
1707    export function createCompletionDetailsForSymbol(symbol: Symbol, checker: TypeChecker, sourceFile: SourceFile, location: Node, cancellationToken: CancellationToken, codeActions?: CodeAction[], sourceDisplay?: SymbolDisplayPart[]): CompletionEntryDetails {
1708        const { displayParts, documentation, symbolKind, tags } =
1709            checker.runWithCancellationToken(cancellationToken, checker =>
1710                SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(checker, symbol, sourceFile, location, location, SemanticMeaning.All)
1711            );
1712        return createCompletionDetails(symbol.name, SymbolDisplay.getSymbolModifiers(checker, symbol), symbolKind, displayParts, documentation, tags, codeActions, sourceDisplay);
1713    }
1714
1715    export function createCompletionDetails(name: string, kindModifiers: string, kind: ScriptElementKind, displayParts: SymbolDisplayPart[], documentation?: SymbolDisplayPart[], tags?: JSDocTagInfo[], codeActions?: CodeAction[], source?: SymbolDisplayPart[]): CompletionEntryDetails {
1716        return { name, kindModifiers, kind, displayParts, documentation, tags, codeActions, source, sourceDisplay: source };
1717    }
1718
1719    interface CodeActionsAndSourceDisplay {
1720        readonly codeActions: CodeAction[] | undefined;
1721        readonly sourceDisplay: SymbolDisplayPart[] | undefined;
1722    }
1723    function getCompletionEntryCodeActionsAndSourceDisplay(
1724        name: string,
1725        location: Node,
1726        contextToken: Node | undefined,
1727        origin: SymbolOriginInfo | SymbolOriginInfoExport | SymbolOriginInfoResolvedExport | undefined,
1728        symbol: Symbol,
1729        program: Program,
1730        host: LanguageServiceHost,
1731        compilerOptions: CompilerOptions,
1732        sourceFile: SourceFile,
1733        position: number,
1734        previousToken: Node | undefined,
1735        formatContext: formatting.FormatContext,
1736        preferences: UserPreferences,
1737        data: CompletionEntryData | undefined,
1738        source: string | undefined,
1739        cancellationToken: CancellationToken,
1740    ): CodeActionsAndSourceDisplay {
1741        if (data?.moduleSpecifier) {
1742            if (previousToken && getImportStatementCompletionInfo(contextToken || previousToken).replacementSpan) {
1743                // Import statement completion: 'import c|'
1744                return { codeActions: undefined, sourceDisplay: [textPart(data.moduleSpecifier)] };
1745            }
1746        }
1747
1748        if (source === CompletionSource.ClassMemberSnippet) {
1749            const { importAdder } = getEntryForMemberCompletion(
1750                host,
1751                program,
1752                compilerOptions,
1753                preferences,
1754                name,
1755                symbol,
1756                location,
1757                contextToken,
1758                formatContext);
1759            if (importAdder) {
1760                const changes = textChanges.ChangeTracker.with(
1761                    { host, formatContext, preferences },
1762                    importAdder.writeFixes);
1763                return {
1764                    sourceDisplay: undefined,
1765                    codeActions: [{
1766                        changes,
1767                        description: diagnosticToString([Diagnostics.Includes_imports_of_types_referenced_by_0, name]),
1768                    }],
1769                };
1770            }
1771        }
1772
1773        if (originIsTypeOnlyAlias(origin)) {
1774            const codeAction = codefix.getPromoteTypeOnlyCompletionAction(
1775                sourceFile,
1776                origin.declaration.name,
1777                program,
1778                host,
1779                formatContext,
1780                preferences);
1781
1782            Debug.assertIsDefined(codeAction, "Expected to have a code action for promoting type-only alias");
1783            return { codeActions: [codeAction], sourceDisplay: undefined };
1784        }
1785
1786        if (!origin || !(originIsExport(origin) || originIsResolvedExport(origin))) {
1787            return { codeActions: undefined, sourceDisplay: undefined };
1788        }
1789
1790        const checker = origin.isFromPackageJson ? host.getPackageJsonAutoImportProvider!()!.getTypeChecker() : program.getTypeChecker();
1791        const { moduleSymbol } = origin;
1792        const targetSymbol = checker.getMergedSymbol(skipAlias(symbol.exportSymbol || symbol, checker));
1793        const isJsxOpeningTagName = contextToken?.kind === SyntaxKind.LessThanToken && isJsxOpeningLikeElement(contextToken.parent);
1794        const { moduleSpecifier, codeAction } = codefix.getImportCompletionAction(
1795            targetSymbol,
1796            moduleSymbol,
1797            sourceFile,
1798            getNameForExportedSymbol(symbol, getEmitScriptTarget(compilerOptions), isJsxOpeningTagName),
1799            isJsxOpeningTagName,
1800            host,
1801            program,
1802            formatContext,
1803            previousToken && isIdentifier(previousToken) ? previousToken.getStart(sourceFile) : position,
1804            preferences,
1805            cancellationToken);
1806        Debug.assert(!data?.moduleSpecifier || moduleSpecifier === data.moduleSpecifier);
1807        return { sourceDisplay: [textPart(moduleSpecifier)], codeActions: [codeAction] };
1808    }
1809
1810    export function getCompletionEntrySymbol(
1811        program: Program,
1812        log: Log,
1813        sourceFile: SourceFile,
1814        position: number,
1815        entryId: CompletionEntryIdentifier,
1816        host: LanguageServiceHost,
1817        preferences: UserPreferences,
1818    ): Symbol | undefined {
1819        const completion = getSymbolCompletionFromEntryId(program, log, sourceFile, position, entryId, host, preferences);
1820        return completion.type === "symbol" ? completion.symbol : undefined;
1821    }
1822
1823    const enum CompletionDataKind { Data, JsDocTagName, JsDocTag, JsDocParameterName, Keywords }
1824    /** true: after the `=` sign but no identifier has been typed yet. Else is the Identifier after the initializer. */
1825    type IsJsxInitializer = boolean | Identifier;
1826    interface CompletionData {
1827        readonly kind: CompletionDataKind.Data;
1828        readonly symbols: readonly Symbol[];
1829        readonly completionKind: CompletionKind;
1830        readonly isInSnippetScope: boolean;
1831        /** 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. */
1832        readonly propertyAccessToConvert: PropertyAccessExpression | undefined;
1833        readonly isNewIdentifierLocation: boolean;
1834        readonly location: Node;
1835        readonly keywordFilters: KeywordCompletionFilters;
1836        readonly literals: readonly (string | number | PseudoBigInt)[];
1837        readonly symbolToOriginInfoMap: SymbolOriginInfoMap;
1838        readonly recommendedCompletion: Symbol | undefined;
1839        readonly previousToken: Node | undefined;
1840        readonly contextToken: Node | undefined;
1841        readonly isJsxInitializer: IsJsxInitializer;
1842        readonly insideJsDocTagTypeExpression: boolean;
1843        readonly symbolToSortTextMap: SymbolSortTextMap;
1844        readonly isTypeOnlyLocation: boolean;
1845        /** In JSX tag name and attribute names, identifiers like "my-tag" or "aria-name" is valid identifier. */
1846        readonly isJsxIdentifierExpected: boolean;
1847        readonly isRightOfOpenTag: boolean;
1848        readonly importStatementCompletion?: ImportStatementCompletionInfo;
1849        readonly hasUnresolvedAutoImports?: boolean;
1850        readonly flags: CompletionInfoFlags;
1851    }
1852    type Request =
1853        | { readonly kind: CompletionDataKind.JsDocTagName | CompletionDataKind.JsDocTag }
1854        | { readonly kind: CompletionDataKind.JsDocParameterName, tag: JSDocParameterTag }
1855        | { readonly kind: CompletionDataKind.Keywords, keywordCompletions: readonly CompletionEntry[], isNewIdentifierLocation: boolean };
1856
1857    export const enum CompletionKind {
1858        ObjectPropertyDeclaration,
1859        Global,
1860        PropertyAccess,
1861        MemberLike,
1862        String,
1863        None,
1864    }
1865
1866    function getRecommendedCompletion(previousToken: Node, contextualType: Type, checker: TypeChecker): Symbol | undefined {
1867        // For a union, return the first one with a recommended completion.
1868        return firstDefined(contextualType && (contextualType.isUnion() ? contextualType.types : [contextualType]), type => {
1869            const symbol = type && type.symbol;
1870            // Don't include make a recommended completion for an abstract class
1871            return symbol && (symbol.flags & (SymbolFlags.EnumMember | SymbolFlags.Enum | SymbolFlags.Class) && !isAbstractConstructorSymbol(symbol))
1872                ? getFirstSymbolInChain(symbol, previousToken, checker)
1873                : undefined;
1874        });
1875    }
1876
1877    function getContextualType(previousToken: Node, position: number, sourceFile: SourceFile, checker: TypeChecker): Type | undefined {
1878        const { parent } = previousToken;
1879        switch (previousToken.kind) {
1880            case SyntaxKind.Identifier:
1881                return getContextualTypeFromParent(previousToken as Identifier, checker);
1882            case SyntaxKind.EqualsToken:
1883                switch (parent.kind) {
1884                    case SyntaxKind.VariableDeclaration:
1885                        return checker.getContextualType((parent as VariableDeclaration).initializer!); // TODO: GH#18217
1886                    case SyntaxKind.BinaryExpression:
1887                        return checker.getTypeAtLocation((parent as BinaryExpression).left);
1888                    case SyntaxKind.JsxAttribute:
1889                        return checker.getContextualTypeForJsxAttribute(parent as JsxAttribute);
1890                    default:
1891                        return undefined;
1892                }
1893            case SyntaxKind.NewKeyword:
1894                return checker.getContextualType(parent as Expression);
1895            case SyntaxKind.CaseKeyword:
1896                const caseClause = tryCast(parent, isCaseClause);
1897                return caseClause ? getSwitchedType(caseClause, checker) : undefined;
1898            case SyntaxKind.OpenBraceToken:
1899                return isJsxExpression(parent) && !isJsxElement(parent.parent) && !isJsxFragment(parent.parent) ? checker.getContextualTypeForJsxAttribute(parent.parent) : undefined;
1900            default:
1901                const argInfo = SignatureHelp.getArgumentInfoForCompletions(previousToken, position, sourceFile);
1902                return argInfo ?
1903                    // At `,`, treat this as the next argument after the comma.
1904                    checker.getContextualTypeForArgumentAtIndex(argInfo.invocation, argInfo.argumentIndex + (previousToken.kind === SyntaxKind.CommaToken ? 1 : 0)) :
1905                    isEqualityOperatorKind(previousToken.kind) && isBinaryExpression(parent) && isEqualityOperatorKind(parent.operatorToken.kind) ?
1906                        // completion at `x ===/**/` should be for the right side
1907                        checker.getTypeAtLocation(parent.left) :
1908                        checker.getContextualType(previousToken as Expression);
1909        }
1910    }
1911
1912    function getFirstSymbolInChain(symbol: Symbol, enclosingDeclaration: Node, checker: TypeChecker): Symbol | undefined {
1913        const chain = checker.getAccessibleSymbolChain(symbol, enclosingDeclaration, /*meaning*/ SymbolFlags.All, /*useOnlyExternalAliasing*/ false);
1914        if (chain) return first(chain);
1915        return symbol.parent && (isModuleSymbol(symbol.parent) ? symbol : getFirstSymbolInChain(symbol.parent, enclosingDeclaration, checker));
1916    }
1917
1918    function isModuleSymbol(symbol: Symbol): boolean {
1919        return !!symbol.declarations?.some(d => d.kind === SyntaxKind.SourceFile);
1920    }
1921
1922    function getCompletionData(
1923        program: Program,
1924        log: (message: string) => void,
1925        sourceFile: SourceFile,
1926        compilerOptions: CompilerOptions,
1927        position: number,
1928        preferences: UserPreferences,
1929        detailsEntryId: CompletionEntryIdentifier | undefined,
1930        host: LanguageServiceHost,
1931        formatContext: formatting.FormatContext | undefined,
1932        cancellationToken?: CancellationToken,
1933    ): CompletionData | Request | undefined {
1934        const isEtsFile = sourceFile.scriptKind === ScriptKind.ETS;
1935        const typeChecker = program.getTypeChecker();
1936        const inCheckedFile = isCheckedFile(sourceFile, compilerOptions);
1937        let start = timestamp();
1938        let currentToken = getTokenAtPosition(sourceFile, position); // TODO: GH#15853
1939        // We will check for jsdoc comments with insideComment and getJsDocTagAtPosition. (TODO: that seems rather inefficient to check the same thing so many times.)
1940
1941        log("getCompletionData: Get current token: " + (timestamp() - start));
1942
1943        start = timestamp();
1944        const insideComment = isInComment(sourceFile, position, currentToken);
1945        log("getCompletionData: Is inside comment: " + (timestamp() - start));
1946
1947        let insideJsDocTagTypeExpression = false;
1948        let isInSnippetScope = false;
1949        if (insideComment) {
1950            if (hasDocComment(sourceFile, position)) {
1951                if (sourceFile.text.charCodeAt(position - 1) === CharacterCodes.at) {
1952                    // The current position is next to the '@' sign, when no tag name being provided yet.
1953                    // Provide a full list of tag names
1954                    return { kind: CompletionDataKind.JsDocTagName };
1955                }
1956                else {
1957                    // When completion is requested without "@", we will have check to make sure that
1958                    // there are no comments prefix the request position. We will only allow "*" and space.
1959                    // e.g
1960                    //   /** |c| /*
1961                    //
1962                    //   /**
1963                    //     |c|
1964                    //    */
1965                    //
1966                    //   /**
1967                    //    * |c|
1968                    //    */
1969                    //
1970                    //   /**
1971                    //    *         |c|
1972                    //    */
1973                    const lineStart = getLineStartPositionForPosition(position, sourceFile);
1974                    if (!/[^\*|\s(/)]/.test(sourceFile.text.substring(lineStart, position))) {
1975                        return { kind: CompletionDataKind.JsDocTag };
1976                    }
1977                }
1978            }
1979
1980            // Completion should work inside certain JsDoc tags. For example:
1981            //     /** @type {number | string} */
1982            // Completion should work in the brackets
1983            const tag = getJsDocTagAtPosition(currentToken, position);
1984            if (tag) {
1985                if (tag.tagName.pos <= position && position <= tag.tagName.end) {
1986                    return { kind: CompletionDataKind.JsDocTagName };
1987                }
1988                const typeExpression = tryGetTypeExpressionFromTag(tag);
1989                if (typeExpression) {
1990                    currentToken = getTokenAtPosition(sourceFile, position);
1991                    if (!currentToken ||
1992                        (!isDeclarationName(currentToken) &&
1993                            (currentToken.parent.kind !== SyntaxKind.JSDocPropertyTag ||
1994                                (currentToken.parent as JSDocPropertyTag).name !== currentToken))) {
1995                        // Use as type location if inside tag's type expression
1996                        insideJsDocTagTypeExpression = isCurrentlyEditingNode(typeExpression);
1997                    }
1998                }
1999                if (!insideJsDocTagTypeExpression && isJSDocParameterTag(tag) && (nodeIsMissing(tag.name) || tag.name.pos <= position && position <= tag.name.end)) {
2000                    return { kind: CompletionDataKind.JsDocParameterName, tag };
2001                }
2002            }
2003
2004            if (!insideJsDocTagTypeExpression) {
2005                // Proceed if the current position is in jsDoc tag expression; otherwise it is a normal
2006                // comment or the plain text part of a jsDoc comment, so no completion should be available
2007                log("Returning an empty list because completion was inside a regular comment or plain text part of a JsDoc comment.");
2008                return undefined;
2009            }
2010        }
2011
2012        start = timestamp();
2013        // The decision to provide completion depends on the contextToken, which is determined through the previousToken.
2014        // Note: 'previousToken' (and thus 'contextToken') can be undefined if we are the beginning of the file
2015        const isJsOnlyLocation = !insideJsDocTagTypeExpression && isSourceFileJS(sourceFile);
2016        const tokens = getRelevantTokens(position, sourceFile);
2017        const previousToken = tokens.previousToken!;
2018        let contextToken = tokens.contextToken!;
2019        log("getCompletionData: Get previous token: " + (timestamp() - start));
2020
2021        // Find the node where completion is requested on.
2022        // Also determine whether we are trying to complete with members of that node
2023        // or attributes of a JSX tag.
2024        let node = currentToken;
2025        let propertyAccessToConvert: PropertyAccessExpression | undefined;
2026        let isRightOfDot = false;
2027        let isRightOfQuestionDot = false;
2028        let isRightOfOpenTag = false;
2029        let isStartingCloseTag = false;
2030        let isJsxInitializer: IsJsxInitializer = false;
2031        let isJsxIdentifierExpected = false;
2032        let importStatementCompletion: ImportStatementCompletionInfo | undefined;
2033        let location = getTouchingPropertyName(sourceFile, position);
2034        let keywordFilters = KeywordCompletionFilters.None;
2035        let isNewIdentifierLocation = false;
2036        let flags = CompletionInfoFlags.None;
2037
2038        if (contextToken) {
2039            const importStatementCompletionInfo = getImportStatementCompletionInfo(contextToken);
2040            if (importStatementCompletionInfo.keywordCompletion) {
2041                if (importStatementCompletionInfo.isKeywordOnlyCompletion) {
2042                    return {
2043                        kind: CompletionDataKind.Keywords,
2044                        keywordCompletions: [keywordToCompletionEntry(importStatementCompletionInfo.keywordCompletion)],
2045                        isNewIdentifierLocation: importStatementCompletionInfo.isNewIdentifierLocation,
2046                    };
2047                }
2048                keywordFilters = keywordFiltersFromSyntaxKind(importStatementCompletionInfo.keywordCompletion);
2049            }
2050            if (importStatementCompletionInfo.replacementSpan && preferences.includeCompletionsForImportStatements && preferences.includeCompletionsWithInsertText) {
2051                // Import statement completions use `insertText`, and also require the `data` property of `CompletionEntryIdentifier`
2052                // added in TypeScript 4.3 to be sent back from the client during `getCompletionEntryDetails`. Since this feature
2053                // is not backward compatible with older clients, the language service defaults to disabling it, allowing newer clients
2054                // to opt in with the `includeCompletionsForImportStatements` user preference.
2055                flags |= CompletionInfoFlags.IsImportStatementCompletion;
2056                importStatementCompletion = importStatementCompletionInfo;
2057                isNewIdentifierLocation = importStatementCompletionInfo.isNewIdentifierLocation;
2058            }
2059            // Bail out if this is a known invalid completion location
2060            if (!importStatementCompletionInfo.replacementSpan && isCompletionListBlocker(contextToken)) {
2061                log("Returning an empty list because completion was requested in an invalid position.");
2062                return keywordFilters
2063                    ? keywordCompletionData(keywordFilters, isJsOnlyLocation, isNewIdentifierDefinitionLocation())
2064                    : undefined;
2065            }
2066
2067            let parent = contextToken.parent;
2068            if (contextToken.kind === SyntaxKind.DotToken || contextToken.kind === SyntaxKind.QuestionDotToken) {
2069                isRightOfDot = contextToken.kind === SyntaxKind.DotToken;
2070                isRightOfQuestionDot = contextToken.kind === SyntaxKind.QuestionDotToken;
2071                switch (parent.kind) {
2072                    case SyntaxKind.PropertyAccessExpression:
2073                        propertyAccessToConvert = parent as PropertyAccessExpression;
2074                        node = propertyAccessToConvert.expression;
2075                        const leftmostAccessExpression = getLeftmostAccessExpression(propertyAccessToConvert);
2076                        if (nodeIsMissing(leftmostAccessExpression) ||
2077                            ((isCallExpression(node) || isFunctionLike(node) || isEtsComponentExpression(node)) &&
2078                                node.end === contextToken.pos &&
2079                                node.getChildCount(sourceFile) &&
2080                                last(node.getChildren(sourceFile)).kind !== SyntaxKind.CloseParenToken && !node.getLastToken(sourceFile))) {
2081                            // This is likely dot from incorrectly parsed expression and user is starting to write spread
2082                            // eg: Math.min(./**/)
2083                            // const x = function (./**/) {}
2084                            // ({./**/})
2085                            return undefined;
2086                        }
2087                        if (node.virtual && findPrecedingToken(node.pos, sourceFile)?.kind === SyntaxKind.OpenParenToken) {
2088                            return undefined;
2089                        }
2090                        break;
2091                    case SyntaxKind.QualifiedName:
2092                        node = (parent as QualifiedName).left;
2093                        break;
2094                    case SyntaxKind.ModuleDeclaration:
2095                        node = (parent as ModuleDeclaration).name;
2096                        break;
2097                    case SyntaxKind.ImportType:
2098                        node = parent;
2099                        break;
2100                    case SyntaxKind.MetaProperty:
2101                        node = parent.getFirstToken(sourceFile)!;
2102                        Debug.assert(node.kind === SyntaxKind.ImportKeyword || node.kind === SyntaxKind.NewKeyword);
2103                        break;
2104                    default:
2105                        // There is nothing that precedes the dot, so this likely just a stray character
2106                        // or leading into a '...' token. Just bail out instead.
2107                        return undefined;
2108                }
2109            }
2110            else if (!importStatementCompletion) {
2111                // <UI.Test /* completion position */ />
2112                // If the tagname is a property access expression, we will then walk up to the top most of property access expression.
2113                // Then, try to get a JSX container and its associated attributes type.
2114                if (parent && parent.kind === SyntaxKind.PropertyAccessExpression) {
2115                    contextToken = parent;
2116                    parent = parent.parent;
2117                }
2118
2119                // Fix location
2120                if (currentToken.parent === location) {
2121                    switch (currentToken.kind) {
2122                        case SyntaxKind.GreaterThanToken:
2123                            if (currentToken.parent.kind === SyntaxKind.JsxElement || currentToken.parent.kind === SyntaxKind.JsxOpeningElement) {
2124                                location = currentToken;
2125                            }
2126                            break;
2127
2128                        case SyntaxKind.SlashToken:
2129                            if (currentToken.parent.kind === SyntaxKind.JsxSelfClosingElement) {
2130                                location = currentToken;
2131                            }
2132                            break;
2133                    }
2134                }
2135
2136                switch (parent.kind) {
2137                    case SyntaxKind.JsxClosingElement:
2138                        if (contextToken.kind === SyntaxKind.SlashToken) {
2139                            isStartingCloseTag = true;
2140                            location = contextToken;
2141                        }
2142                        break;
2143
2144                    case SyntaxKind.BinaryExpression:
2145                        if (!binaryExpressionMayBeOpenTag(parent as BinaryExpression)) {
2146                            break;
2147                        }
2148                    // falls through
2149
2150                    case SyntaxKind.JsxSelfClosingElement:
2151                    case SyntaxKind.JsxElement:
2152                    case SyntaxKind.JsxOpeningElement:
2153                        isJsxIdentifierExpected = true;
2154                        if (contextToken.kind === SyntaxKind.LessThanToken) {
2155                            isRightOfOpenTag = true;
2156                            location = contextToken;
2157                        }
2158                        break;
2159
2160                    case SyntaxKind.JsxExpression:
2161                    case SyntaxKind.JsxSpreadAttribute:
2162                        // For `<div foo={true} [||] ></div>`, `parent` will be `{true}` and `previousToken` will be `}`
2163                        if (previousToken.kind === SyntaxKind.CloseBraceToken && currentToken.kind === SyntaxKind.GreaterThanToken) {
2164                            isJsxIdentifierExpected = true;
2165                        }
2166                        break;
2167
2168                    case SyntaxKind.JsxAttribute:
2169                        // For `<div className="x" [||] ></div>`, `parent` will be JsxAttribute and `previousToken` will be its initializer
2170                        if ((parent as JsxAttribute).initializer === previousToken &&
2171                            previousToken.end < position) {
2172                            isJsxIdentifierExpected = true;
2173                            break;
2174                        }
2175                        switch (previousToken.kind) {
2176                            case SyntaxKind.EqualsToken:
2177                                isJsxInitializer = true;
2178                                break;
2179                            case SyntaxKind.Identifier:
2180                                isJsxIdentifierExpected = true;
2181                                // For `<div x=[|f/**/|]`, `parent` will be `x` and `previousToken.parent` will be `f` (which is its own JsxAttribute)
2182                                // Note for `<div someBool f>` we don't want to treat this as a jsx inializer, instead it's the attribute name.
2183                                if (parent !== previousToken.parent &&
2184                                    !(parent as JsxAttribute).initializer &&
2185                                    findChildOfKind(parent, SyntaxKind.EqualsToken, sourceFile)) {
2186                                    isJsxInitializer = previousToken as Identifier;
2187                                }
2188                        }
2189                        break;
2190                }
2191            }
2192        }
2193
2194        const semanticStart = timestamp();
2195        let completionKind = CompletionKind.None;
2196        let isNonContextualObjectLiteral = false;
2197        let hasUnresolvedAutoImports = false;
2198        // This also gets mutated in nested-functions after the return
2199        let symbols: Symbol[] = [];
2200        let importSpecifierResolver: codefix.ImportSpecifierResolver | undefined;
2201        const symbolToOriginInfoMap: SymbolOriginInfoMap = [];
2202        const symbolToSortTextMap: SymbolSortTextMap = [];
2203        const seenPropertySymbols = new Map<SymbolId, true>();
2204        const isTypeOnlyLocation = isTypeOnlyCompletion();
2205        const getModuleSpecifierResolutionHost = memoizeOne((isFromPackageJson: boolean) => {
2206            return createModuleSpecifierResolutionHost(isFromPackageJson ? host.getPackageJsonAutoImportProvider!()! : program, host);
2207        });
2208
2209        if (isRightOfDot || isRightOfQuestionDot) {
2210            getTypeScriptMemberSymbols();
2211        }
2212        else if (isRightOfOpenTag) {
2213            symbols = typeChecker.getJsxIntrinsicTagNamesAt(location);
2214            Debug.assertEachIsDefined(symbols, "getJsxIntrinsicTagNames() should all be defined");
2215            tryGetGlobalSymbols();
2216            completionKind = CompletionKind.Global;
2217            keywordFilters = KeywordCompletionFilters.None;
2218        }
2219        else if (isStartingCloseTag) {
2220            const tagName = (contextToken.parent.parent as JsxElement).openingElement.tagName;
2221            const tagSymbol = typeChecker.getSymbolAtLocation(tagName);
2222            if (tagSymbol) {
2223                symbols = [tagSymbol];
2224            }
2225            completionKind = CompletionKind.Global;
2226            keywordFilters = KeywordCompletionFilters.None;
2227        }
2228        else {
2229            // For JavaScript or TypeScript, if we're not after a dot, then just try to get the
2230            // global symbols in scope.  These results should be valid for either language as
2231            // the set of symbols that can be referenced from this location.
2232            if (!tryGetGlobalSymbols()) {
2233                return keywordFilters
2234                    ? keywordCompletionData(keywordFilters, isJsOnlyLocation, isNewIdentifierLocation)
2235                    : undefined;
2236            }
2237        }
2238
2239        const etsLibFilesNames = program.getEtsLibSFromProgram();
2240        symbols = symbols.filter(symbol => {
2241            if(!symbol.declarations || !symbol.declarations.length) {
2242                return true;
2243            }
2244            const declaration = (symbol.declarations??[]).filter(declaration =>{
2245                if(!declaration.getSourceFile().fileName) {
2246                    return true;
2247                }
2248                const symbolFileName = sys.resolvePath(declaration.getSourceFile().fileName);
2249                if(!isEtsFile && etsLibFilesNames.indexOf(symbolFileName) !== -1) {
2250                    return false;
2251                }
2252                return true;
2253            });
2254            return declaration.length;
2255        });
2256
2257        log("getCompletionData: Semantic work: " + (timestamp() - semanticStart));
2258        const contextualType = previousToken && getContextualType(previousToken, position, sourceFile, typeChecker);
2259
2260        const literals = mapDefined(
2261            contextualType && (contextualType.isUnion() ? contextualType.types : [contextualType]),
2262            t => t.isLiteral() && !(t.flags & TypeFlags.EnumLiteral) ? t.value : undefined);
2263
2264        const recommendedCompletion = previousToken && contextualType && getRecommendedCompletion(previousToken, contextualType, typeChecker);
2265        return {
2266            kind: CompletionDataKind.Data,
2267            symbols,
2268            completionKind,
2269            isInSnippetScope,
2270            propertyAccessToConvert,
2271            isNewIdentifierLocation,
2272            location,
2273            keywordFilters,
2274            literals,
2275            symbolToOriginInfoMap,
2276            recommendedCompletion,
2277            previousToken,
2278            contextToken,
2279            isJsxInitializer,
2280            insideJsDocTagTypeExpression,
2281            symbolToSortTextMap,
2282            isTypeOnlyLocation,
2283            isJsxIdentifierExpected,
2284            isRightOfOpenTag,
2285            importStatementCompletion,
2286            hasUnresolvedAutoImports,
2287            flags,
2288        };
2289
2290        type JSDocTagWithTypeExpression = JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag | JSDocTemplateTag;
2291
2292        function isTagWithTypeExpression(tag: JSDocTag): tag is JSDocTagWithTypeExpression {
2293            switch (tag.kind) {
2294                case SyntaxKind.JSDocParameterTag:
2295                case SyntaxKind.JSDocPropertyTag:
2296                case SyntaxKind.JSDocReturnTag:
2297                case SyntaxKind.JSDocTypeTag:
2298                case SyntaxKind.JSDocTypedefTag:
2299                    return true;
2300                case SyntaxKind.JSDocTemplateTag:
2301                    return !!(tag as JSDocTemplateTag).constraint;
2302                default:
2303                    return false;
2304            }
2305        }
2306
2307        function tryGetTypeExpressionFromTag(tag: JSDocTag): JSDocTypeExpression | undefined {
2308            if (isTagWithTypeExpression(tag)) {
2309                const typeExpression = isJSDocTemplateTag(tag) ? tag.constraint : tag.typeExpression;
2310                return typeExpression && typeExpression.kind === SyntaxKind.JSDocTypeExpression ? typeExpression : undefined;
2311            }
2312            return undefined;
2313        }
2314
2315        function getTypeScriptMemberSymbols(): void {
2316            // Right of dot member completion list
2317            completionKind = CompletionKind.PropertyAccess;
2318
2319            // Since this is qualified name check it's a type node location
2320            const isImportType = isLiteralImportTypeNode(node);
2321            const isTypeLocation = insideJsDocTagTypeExpression
2322                || (isImportType && !(node as ImportTypeNode).isTypeOf)
2323                || isPartOfTypeNode(node.parent)
2324                || isPossiblyTypeArgumentPosition(contextToken, sourceFile, typeChecker);
2325            const isRhsOfImportDeclaration = isInRightSideOfInternalImportEqualsDeclaration(node);
2326            if (isEntityName(node) || isImportType || isPropertyAccessExpression(node)) {
2327                const isNamespaceName = isModuleDeclaration(node.parent);
2328                if (isNamespaceName) isNewIdentifierLocation = true;
2329                let symbol = typeChecker.getSymbolAtLocation(node);
2330                if (symbol) {
2331                    symbol = skipAlias(symbol, typeChecker);
2332                    if (symbol.flags & (SymbolFlags.Module | SymbolFlags.Enum)) {
2333                        // Extract module or enum members
2334                        const exportedSymbols = typeChecker.getExportsOfModule(symbol);
2335                        Debug.assertEachIsDefined(exportedSymbols, "getExportsOfModule() should all be defined");
2336                        const isValidValueAccess = (symbol: Symbol) => typeChecker.isValidPropertyAccess(isImportType ? node as ImportTypeNode : (node.parent as PropertyAccessExpression), symbol.name);
2337                        const isValidTypeAccess = (symbol: Symbol) => symbolCanBeReferencedAtTypeLocation(symbol, typeChecker);
2338                        const isValidAccess: (symbol: Symbol) => boolean =
2339                            isNamespaceName
2340                                // At `namespace N.M/**/`, if this is the only declaration of `M`, don't include `M` as a completion.
2341                                ? symbol => !!(symbol.flags & SymbolFlags.Namespace) && !symbol.declarations?.every(d => d.parent === node.parent)
2342                                : isRhsOfImportDeclaration ?
2343                                    // Any kind is allowed when dotting off namespace in internal import equals declaration
2344                                    symbol => isValidTypeAccess(symbol) || isValidValueAccess(symbol) :
2345                                    isTypeLocation ? isValidTypeAccess : isValidValueAccess;
2346                        for (const exportedSymbol of exportedSymbols) {
2347                            if (isValidAccess(exportedSymbol)) {
2348                                symbols.push(exportedSymbol);
2349                            }
2350                        }
2351
2352                        // If the module is merged with a value, we must get the type of the class and add its propertes (for inherited static methods).
2353                        if (!isTypeLocation &&
2354                            symbol.declarations &&
2355                            symbol.declarations.some(d => d.kind !== SyntaxKind.SourceFile && d.kind !== SyntaxKind.ModuleDeclaration && d.kind !== SyntaxKind.EnumDeclaration)) {
2356                            let type = typeChecker.getTypeOfSymbolAtLocation(symbol, node).getNonOptionalType();
2357                            let insertQuestionDot = false;
2358                            if (type.isNullableType()) {
2359                                const canCorrectToQuestionDot =
2360                                    isRightOfDot &&
2361                                    !isRightOfQuestionDot &&
2362                                    preferences.includeAutomaticOptionalChainCompletions !== false;
2363
2364                                if (canCorrectToQuestionDot || isRightOfQuestionDot) {
2365                                    type = type.getNonNullableType();
2366                                    if (canCorrectToQuestionDot) {
2367                                        insertQuestionDot = true;
2368                                    }
2369                                }
2370                            }
2371                            addTypeProperties(type, !!(node.flags & NodeFlags.AwaitContext), insertQuestionDot);
2372                        }
2373
2374                        return;
2375                    }
2376                }
2377            }
2378
2379            if (!isTypeLocation) {
2380                // GH#39946. Pulling on the type of a node inside of a function with a contextual `this` parameter can result in a circularity
2381                // if the `node` is part of the exprssion of a `yield` or `return`. This circularity doesn't exist at compile time because
2382                // we will check (and cache) the type of `this` *before* checking the type of the node.
2383                typeChecker.tryGetThisTypeAt(node, /*includeGlobalThis*/ false);
2384
2385                let type = typeChecker.getTypeAtLocation(node).getNonOptionalType();
2386                let insertQuestionDot = false;
2387                if (type.isNullableType()) {
2388                    const canCorrectToQuestionDot =
2389                        isRightOfDot &&
2390                        !isRightOfQuestionDot &&
2391                        preferences.includeAutomaticOptionalChainCompletions !== false;
2392
2393                    if (canCorrectToQuestionDot || isRightOfQuestionDot) {
2394                        type = type.getNonNullableType();
2395                        if (canCorrectToQuestionDot) {
2396                            insertQuestionDot = true;
2397                        }
2398                    }
2399                }
2400                addTypeProperties(type, !!(node.flags & NodeFlags.AwaitContext), insertQuestionDot);
2401            }
2402        }
2403
2404        function addTypeProperties(type: Type, insertAwait: boolean, insertQuestionDot: boolean): void {
2405            isNewIdentifierLocation = !!type.getStringIndexType();
2406            if (isRightOfQuestionDot && some(type.getCallSignatures())) {
2407                isNewIdentifierLocation = true;
2408            }
2409
2410            const propertyAccess = node.kind === SyntaxKind.ImportType ? node as ImportTypeNode : node.parent as PropertyAccessExpression | QualifiedName;
2411            if (inCheckedFile) {
2412                const typeSymbols = type.getApparentProperties();
2413                for (const symbol of typeSymbols) {
2414                    if (typeChecker.isValidPropertyAccessForCompletions(propertyAccess, type, symbol)) {
2415                        addPropertySymbol(symbol, /* insertAwait */ false, insertQuestionDot);
2416                    }
2417                }
2418
2419                // The extension method on the ETS depends on whether the type is correctly parsed.
2420                if (typeSymbols.length) {
2421                    // if complete expression is ets component expression, then complete data need add extend properties and styles properties.
2422                    const etsComponentExpressionNode = getEtsComponentExpressionInnerExpressionStatementNode(node)
2423                        || getRootEtsComponentInnerCallExpressionNode(node);
2424                    const returnType = typeChecker.getTypeAtLocation(node);
2425                    if (etsComponentExpressionNode && shouldAddExtendOrStylesProperties(node, returnType)) {
2426                        addEtsExtendPropertySymbol(etsComponentExpressionNode, insertQuestionDot);
2427                        addEtsStylesPropertySymbol(etsComponentExpressionNode, insertQuestionDot);
2428                    }
2429                }
2430            }
2431            else {
2432                // In javascript files, for union types, we don't just get the members that
2433                // the individual types have in common, we also include all the members that
2434                // each individual type has. This is because we're going to add all identifiers
2435                // anyways. So we might as well elevate the members that were at least part
2436                // of the individual types to a higher status since we know what they are.
2437                symbols.push(...filter(getPropertiesForCompletion(type, typeChecker), s => typeChecker.isValidPropertyAccessForCompletions(propertyAccess, type, s)));
2438            }
2439
2440            if (insertAwait && preferences.includeCompletionsWithInsertText) {
2441                const promiseType = typeChecker.getPromisedTypeOfPromise(type);
2442                if (promiseType) {
2443                    for (const symbol of promiseType.getApparentProperties()) {
2444                        if (typeChecker.isValidPropertyAccessForCompletions(propertyAccess, promiseType, symbol)) {
2445                            addPropertySymbol(symbol, /* insertAwait */ true, insertQuestionDot);
2446                        }
2447                    }
2448                }
2449            }
2450        }
2451
2452        function shouldAddExtendOrStylesProperties(node: Node, returnType: Type) {
2453            return isCallExpressionOrEtsComponentExpressionKind(node, returnType) &&
2454                !!returnType.symbol.declarations?.length &&
2455                !isStructDeclaration(returnType.symbol.declarations[0]);
2456        }
2457
2458        function isCallExpressionOrEtsComponentExpressionKind(node: Node, returnType: Type):boolean {
2459            if ((isCallExpression(node) || isEtsComponentExpression(node)) && returnType.symbol) {
2460                return !!returnType.symbol.getName().match("Attribute") && isVirtualAttributeTypeArgument(node);
2461            }
2462            return !!node.virtual && isIdentifier(node) && !!node.escapedText.toString().match("Instance");
2463        }
2464
2465        function addPropertySymbol(symbol: Symbol, insertAwait: boolean, insertQuestionDot: boolean) {
2466            // For a computed property with an accessible name like `Symbol.iterator`,
2467            // we'll add a completion for the *name* `Symbol` instead of for the property.
2468            // If this is e.g. [Symbol.iterator], add a completion for `Symbol`.
2469            const computedPropertyName = firstDefined(symbol.declarations, decl => tryCast(getNameOfDeclaration(decl), isComputedPropertyName));
2470            if (computedPropertyName) {
2471                const leftMostName = getLeftMostName(computedPropertyName.expression); // The completion is for `Symbol`, not `iterator`.
2472                const nameSymbol = leftMostName && typeChecker.getSymbolAtLocation(leftMostName);
2473                // If this is nested like for `namespace N { export const sym = Symbol(); }`, we'll add the completion for `N`.
2474                const firstAccessibleSymbol = nameSymbol && getFirstSymbolInChain(nameSymbol, contextToken, typeChecker);
2475                if (firstAccessibleSymbol && addToSeen(seenPropertySymbols, getSymbolId(firstAccessibleSymbol))) {
2476                    const index = symbols.length;
2477                    symbols.push(firstAccessibleSymbol);
2478                    const moduleSymbol = firstAccessibleSymbol.parent;
2479                    if (!moduleSymbol ||
2480                        !isExternalModuleSymbol(moduleSymbol) ||
2481                        typeChecker.tryGetMemberInModuleExportsAndProperties(firstAccessibleSymbol.name, moduleSymbol) !== firstAccessibleSymbol
2482                    ) {
2483                        symbolToOriginInfoMap[index] = { kind: getNullableSymbolOriginInfoKind(SymbolOriginInfoKind.SymbolMemberNoExport) };
2484                    }
2485                    else {
2486                        const fileName = isExternalModuleNameRelative(stripQuotes(moduleSymbol.name)) ? getSourceFileOfModule(moduleSymbol)?.fileName : undefined;
2487                        const { moduleSpecifier } = (importSpecifierResolver ||= codefix.createImportSpecifierResolver(sourceFile, program, host, preferences)).getModuleSpecifierForBestExportInfo([{
2488                            exportKind: ExportKind.Named,
2489                            moduleFileName: fileName,
2490                            isFromPackageJson: false,
2491                            moduleSymbol,
2492                            symbol: firstAccessibleSymbol,
2493                            targetFlags: skipAlias(firstAccessibleSymbol, typeChecker).flags,
2494                        }], firstAccessibleSymbol.name, position, isValidTypeOnlyAliasUseSite(location)) || {};
2495
2496                        if (moduleSpecifier) {
2497                            const origin: SymbolOriginInfoResolvedExport = {
2498                                kind: getNullableSymbolOriginInfoKind(SymbolOriginInfoKind.SymbolMemberExport),
2499                                moduleSymbol,
2500                                isDefaultExport: false,
2501                                symbolName: firstAccessibleSymbol.name,
2502                                exportName: firstAccessibleSymbol.name,
2503                                fileName,
2504                                moduleSpecifier,
2505                            };
2506                            symbolToOriginInfoMap[index] = origin;
2507                        }
2508                    }
2509                }
2510                else if (preferences.includeCompletionsWithInsertText) {
2511                    addSymbolOriginInfo(symbol);
2512                    addSymbolSortInfo(symbol);
2513                    symbols.push(symbol);
2514                }
2515            }
2516            else {
2517                addSymbolOriginInfo(symbol);
2518                addSymbolSortInfo(symbol);
2519                symbols.push(symbol);
2520            }
2521
2522            function addSymbolSortInfo(symbol: Symbol) {
2523                if (isStaticProperty(symbol)) {
2524                    symbolToSortTextMap[getSymbolId(symbol)] = SortText.LocalDeclarationPriority;
2525                }
2526            }
2527
2528            function addSymbolOriginInfo(symbol: Symbol) {
2529                if (preferences.includeCompletionsWithInsertText) {
2530                    if (insertAwait && addToSeen(seenPropertySymbols, getSymbolId(symbol))) {
2531                        symbolToOriginInfoMap[symbols.length] = { kind: getNullableSymbolOriginInfoKind(SymbolOriginInfoKind.Promise) };
2532                    }
2533                    else if (insertQuestionDot) {
2534                        symbolToOriginInfoMap[symbols.length] = { kind: SymbolOriginInfoKind.Nullable };
2535                    }
2536                }
2537            }
2538
2539            function getNullableSymbolOriginInfoKind(kind: SymbolOriginInfoKind) {
2540                return insertQuestionDot ? kind | SymbolOriginInfoKind.Nullable : kind;
2541            }
2542        }
2543
2544        function addEtsExtendPropertySymbol(node: EtsComponentExpression | CallExpression | PropertyAccessExpression | Identifier, insertQuestionDot: boolean) {
2545            const locals = getSourceFileOfNode(node).locals;
2546            if (!locals) {
2547                return;
2548            }
2549            const etsComponentName = isIdentifier(node) ? node.escapedText : isIdentifier(node.expression) ? node.expression.escapedText : undefined;
2550            const extendComponentSymbolMap: UnderscoreEscapedMap<Symbol[]> = new Map<__String, Symbol[]>();
2551            locals.forEach(local => {
2552                const declaration = getDeclarationFromSymbol(local);
2553                if (!declaration) {
2554                    return;
2555                }
2556                const currDeclaration = isVariableDeclaration(declaration) && isVariableDeclarationList(declaration.parent) && isVariableStatement(declaration.parent.parent) ? declaration.parent.parent : declaration;
2557                getEtsExtendDecoratorsComponentNames(getAllDecorators(currDeclaration), compilerOptions).forEach((extendName) => {
2558                    if (extendComponentSymbolMap.has(extendName)) {
2559                        extendComponentSymbolMap.get(extendName)!.push(local);
2560                    }
2561                    else {
2562                        extendComponentSymbolMap.set(extendName, [local]);
2563                    }
2564                });
2565            });
2566            if (!etsComponentName) {
2567                return;
2568            }
2569            const name = extendComponentSymbolMap.has(etsComponentName) ? 
2570                etsComponentName : extendComponentSymbolMap.has(etsComponentName.toString().slice(0, -8) as __String) ? 
2571                    etsComponentName.toString().slice(0, -8) as __String : undefined;
2572            if (!name) {
2573                return;
2574            }
2575            extendComponentSymbolMap.get(name)!.forEach(local => {
2576                addPropertySymbol(local, /* insertAwait */ false, insertQuestionDot);
2577            });
2578        }
2579
2580        function addEtsStylesPropertySymbol(node: EtsComponentExpression | CallExpression | PropertyAccessExpression | Identifier , insertQuestionDot: boolean) {
2581            const locals = getSourceFileOfNode(node).locals;
2582            if (!locals) {
2583                return;
2584            }
2585            const etsComponentName = isIdentifier(node) ? node.escapedText : isIdentifier(node.expression) ? node.expression.escapedText : undefined;
2586            const stylesComponentSymbolMap: UnderscoreEscapedMap<Symbol[]> = new Map<__String, Symbol[]>();
2587            locals.forEach(local => {
2588                const declaration = getDeclarationFromSymbol(local);
2589                if (!declaration) {
2590                    return;
2591                }
2592                const currDeclaration = isVariableDeclaration(declaration) && isVariableDeclarationList(declaration.parent) && isVariableStatement(declaration.parent.parent) ? declaration.parent.parent : declaration;
2593                getEtsStylesDecoratorComponentNames(getAllDecorators(currDeclaration), compilerOptions).forEach((stylesName) => {
2594                    if (stylesComponentSymbolMap.has(stylesName)) {
2595                        stylesComponentSymbolMap.get(stylesName)!.push(local);
2596                    }
2597                    else {
2598                        stylesComponentSymbolMap.set(stylesName, [local]);
2599                    }
2600                });
2601            });
2602            // If it's a '@Styles' method inside StructDeclaration,
2603            // we will find container StructDeclaration of current node first,
2604            // and then find method decorated with '@Styles'
2605            getContainingStruct(node)?.symbol.members?.forEach(member => {
2606                getEtsStylesDecoratorComponentNames(getAllDecorators(member.valueDeclaration), compilerOptions).forEach((stylesName) => {
2607                    if (stylesComponentSymbolMap.has(stylesName)) {
2608                        stylesComponentSymbolMap.get(stylesName)!.push(member);
2609                    }
2610                    else {
2611                        stylesComponentSymbolMap.set(stylesName, [member]);
2612                    }
2613                });
2614            });
2615            if (etsComponentName && stylesComponentSymbolMap.size > 0) {
2616                stylesComponentSymbolMap.forEach(symbols => {
2617                    symbols.forEach(symbol => {
2618                        addPropertySymbol(symbol, /* insertAwait */ false, insertQuestionDot);
2619                    });
2620                });
2621            }
2622        }
2623
2624        /** Given 'a.b.c', returns 'a'. */
2625        function getLeftMostName(e: Expression): Identifier | undefined {
2626            return isIdentifier(e) ? e : isPropertyAccessExpression(e) ? getLeftMostName(e.expression) : undefined;
2627        }
2628
2629        function tryGetGlobalSymbols(): boolean {
2630            const result: GlobalsSearch = tryGetObjectTypeLiteralInTypeArgumentCompletionSymbols()
2631                || tryGetObjectLikeCompletionSymbols()
2632                || tryGetImportCompletionSymbols()
2633                || tryGetImportOrExportClauseCompletionSymbols()
2634                || tryGetLocalNamedExportCompletionSymbols()
2635                || tryGetConstructorCompletion()
2636                || tryGetClassLikeCompletionSymbols()
2637                || tryGetJsxCompletionSymbols()
2638                || (getGlobalCompletions(), GlobalsSearch.Success);
2639            return result === GlobalsSearch.Success;
2640        }
2641
2642        function tryGetConstructorCompletion(): GlobalsSearch {
2643            if (!tryGetConstructorLikeCompletionContainer(contextToken)) return GlobalsSearch.Continue;
2644            // no members, only keywords
2645            completionKind = CompletionKind.None;
2646            // Declaring new property/method/accessor
2647            isNewIdentifierLocation = true;
2648            // Has keywords for constructor parameter
2649            keywordFilters = KeywordCompletionFilters.ConstructorParameterKeywords;
2650            return GlobalsSearch.Success;
2651        }
2652
2653        function tryGetJsxCompletionSymbols(): GlobalsSearch {
2654            const jsxContainer = tryGetContainingJsxElement(contextToken);
2655            // Cursor is inside a JSX self-closing element or opening element
2656            const attrsType = jsxContainer && typeChecker.getContextualType(jsxContainer.attributes);
2657            if (!attrsType) return GlobalsSearch.Continue;
2658            const completionsType = jsxContainer && typeChecker.getContextualType(jsxContainer.attributes, ContextFlags.Completions);
2659            symbols = concatenate(symbols, filterJsxAttributes(getPropertiesForObjectExpression(attrsType, completionsType, jsxContainer.attributes, typeChecker), jsxContainer.attributes.properties));
2660            setSortTextToOptionalMember();
2661            completionKind = CompletionKind.MemberLike;
2662            isNewIdentifierLocation = false;
2663            return GlobalsSearch.Success;
2664        }
2665
2666        function tryGetImportCompletionSymbols(): GlobalsSearch {
2667            if (!importStatementCompletion) return GlobalsSearch.Continue;
2668            isNewIdentifierLocation = true;
2669            collectAutoImports();
2670            return GlobalsSearch.Success;
2671        }
2672
2673        function getGlobalCompletions(): void {
2674            keywordFilters = tryGetFunctionLikeBodyCompletionContainer(contextToken) ? KeywordCompletionFilters.FunctionLikeBodyKeywords : KeywordCompletionFilters.All;
2675
2676            // Get all entities in the current scope.
2677            completionKind = CompletionKind.Global;
2678            isNewIdentifierLocation = isNewIdentifierDefinitionLocation();
2679
2680            if (previousToken !== contextToken) {
2681                Debug.assert(!!previousToken, "Expected 'contextToken' to be defined when different from 'previousToken'.");
2682            }
2683            // We need to find the node that will give us an appropriate scope to begin
2684            // aggregating completion candidates. This is achieved in 'getScopeNode'
2685            // by finding the first node that encompasses a position, accounting for whether a node
2686            // is "complete" to decide whether a position belongs to the node.
2687            //
2688            // However, at the end of an identifier, we are interested in the scope of the identifier
2689            // itself, but fall outside of the identifier. For instance:
2690            //
2691            //      xyz => x$
2692            //
2693            // the cursor is outside of both the 'x' and the arrow function 'xyz => x',
2694            // so 'xyz' is not returned in our results.
2695            //
2696            // We define 'adjustedPosition' so that we may appropriately account for
2697            // being at the end of an identifier. The intention is that if requesting completion
2698            // at the end of an identifier, it should be effectively equivalent to requesting completion
2699            // anywhere inside/at the beginning of the identifier. So in the previous case, the
2700            // 'adjustedPosition' will work as if requesting completion in the following:
2701            //
2702            //      xyz => $x
2703            //
2704            // If previousToken !== contextToken, then
2705            //   - 'contextToken' was adjusted to the token prior to 'previousToken'
2706            //      because we were at the end of an identifier.
2707            //   - 'previousToken' is defined.
2708            const adjustedPosition = previousToken !== contextToken ?
2709                previousToken.getStart() :
2710                position;
2711
2712            const scopeNode = getScopeNode(contextToken, adjustedPosition, sourceFile) || sourceFile;
2713            isInSnippetScope = isSnippetScope(scopeNode);
2714
2715            const symbolMeanings = (isTypeOnlyLocation ? SymbolFlags.None : SymbolFlags.Value) | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias;
2716            const typeOnlyAliasNeedsPromotion = previousToken && !isValidTypeOnlyAliasUseSite(previousToken);
2717
2718            symbols = concatenate(symbols, typeChecker.getSymbolsInScope(scopeNode, symbolMeanings));
2719            Debug.assertEachIsDefined(symbols, "getSymbolsInScope() should all be defined");
2720            for (let i = 0; i < symbols.length; i++) {
2721                const symbol = symbols[i];
2722                if (!typeChecker.isArgumentsSymbol(symbol) &&
2723                    !some(symbol.declarations, d => d.getSourceFile() === sourceFile)) {
2724                    symbolToSortTextMap[getSymbolId(symbol)] = SortText.GlobalsOrKeywords;
2725                }
2726                if (typeOnlyAliasNeedsPromotion && !(symbol.flags & SymbolFlags.Value)) {
2727                    const typeOnlyAliasDeclaration = symbol.declarations && find(symbol.declarations, isTypeOnlyImportOrExportDeclaration);
2728                    if (typeOnlyAliasDeclaration) {
2729                        const origin: SymbolOriginInfoTypeOnlyAlias = { kind: SymbolOriginInfoKind.TypeOnlyAlias, declaration: typeOnlyAliasDeclaration };
2730                        symbolToOriginInfoMap[i] = origin;
2731                    }
2732                }
2733            }
2734
2735            // Need to insert 'this.' before properties of `this` type, so only do that if `includeInsertTextCompletions`
2736            if (preferences.includeCompletionsWithInsertText && scopeNode.kind !== SyntaxKind.SourceFile) {
2737                const thisType = typeChecker.tryGetThisTypeAt(scopeNode, /*includeGlobalThis*/ false, isClassLike(scopeNode.parent) ? scopeNode : undefined);
2738                if (thisType && !isProbablyGlobalType(thisType, sourceFile, typeChecker)) {
2739                    for (const symbol of getPropertiesForCompletion(thisType, typeChecker)) {
2740                        symbolToOriginInfoMap[symbols.length] = { kind: SymbolOriginInfoKind.ThisType };
2741                        symbols.push(symbol);
2742                        symbolToSortTextMap[getSymbolId(symbol)] = SortText.SuggestedClassMembers;
2743                    }
2744                }
2745            }
2746            collectAutoImports();
2747            if (isTypeOnlyLocation) {
2748                keywordFilters = contextToken && isAssertionExpression(contextToken.parent)
2749                    ? KeywordCompletionFilters.TypeAssertionKeywords
2750                    : KeywordCompletionFilters.TypeKeywords;
2751            }
2752        }
2753
2754        function shouldOfferImportCompletions(): boolean {
2755            // If already typing an import statement, provide completions for it.
2756            if (importStatementCompletion) return true;
2757            // If current completion is for non-contextual Object literal shortahands, ignore auto-import symbols
2758            if (isNonContextualObjectLiteral) return false;
2759            // If not already a module, must have modules enabled.
2760            if (!preferences.includeCompletionsForModuleExports) return false;
2761            // If already using ES modules, OK to continue using them.
2762            if (sourceFile.externalModuleIndicator || sourceFile.commonJsModuleIndicator) return true;
2763            // If module transpilation is enabled or we're targeting es6 or above, or not emitting, OK.
2764            if (compilerOptionsIndicateEsModules(program.getCompilerOptions())) return true;
2765            // If some file is using ES6 modules, assume that it's OK to add more.
2766            return programContainsModules(program);
2767        }
2768
2769        function isSnippetScope(scopeNode: Node): boolean {
2770            switch (scopeNode.kind) {
2771                case SyntaxKind.SourceFile:
2772                case SyntaxKind.TemplateExpression:
2773                case SyntaxKind.JsxExpression:
2774                case SyntaxKind.Block:
2775                    return true;
2776                default:
2777                    return isStatement(scopeNode);
2778            }
2779        }
2780
2781        function isTypeOnlyCompletion(): boolean {
2782            return insideJsDocTagTypeExpression
2783                || !!importStatementCompletion && isTypeOnlyImportOrExportDeclaration(location.parent)
2784                || !isContextTokenValueLocation(contextToken) &&
2785                (isPossiblyTypeArgumentPosition(contextToken, sourceFile, typeChecker)
2786                    || isPartOfTypeNode(location)
2787                    || isContextTokenTypeLocation(contextToken));
2788        }
2789
2790        function isContextTokenValueLocation(contextToken: Node) {
2791            return contextToken &&
2792                ((contextToken.kind === SyntaxKind.TypeOfKeyword &&
2793                    (contextToken.parent.kind === SyntaxKind.TypeQuery || isTypeOfExpression(contextToken.parent))) ||
2794                (contextToken.kind === SyntaxKind.AssertsKeyword && contextToken.parent.kind === SyntaxKind.TypePredicate));
2795        }
2796
2797        function isContextTokenTypeLocation(contextToken: Node): boolean {
2798            if (contextToken) {
2799                const parentKind = contextToken.parent.kind;
2800                switch (contextToken.kind) {
2801                    case SyntaxKind.ColonToken:
2802                        return parentKind === SyntaxKind.PropertyDeclaration ||
2803                            parentKind === SyntaxKind.PropertySignature ||
2804                            parentKind === SyntaxKind.Parameter ||
2805                            parentKind === SyntaxKind.VariableDeclaration ||
2806                            isFunctionLikeKind(parentKind);
2807
2808                    case SyntaxKind.EqualsToken:
2809                        return parentKind === SyntaxKind.TypeAliasDeclaration;
2810
2811                    case SyntaxKind.AsKeyword:
2812                        return parentKind === SyntaxKind.AsExpression;
2813
2814                    case SyntaxKind.LessThanToken:
2815                        return parentKind === SyntaxKind.TypeReference ||
2816                            parentKind === SyntaxKind.TypeAssertionExpression;
2817
2818                    case SyntaxKind.ExtendsKeyword:
2819                        return parentKind === SyntaxKind.TypeParameter;
2820
2821                    case SyntaxKind.SatisfiesKeyword:
2822                        return parentKind === SyntaxKind.SatisfiesExpression;
2823                }
2824            }
2825            return false;
2826        }
2827
2828        /** Mutates `symbols`, `symbolToOriginInfoMap`, and `symbolToSortTextMap` */
2829        function collectAutoImports() {
2830            if (!shouldOfferImportCompletions()) return;
2831            Debug.assert(!detailsEntryId?.data, "Should not run 'collectAutoImports' when faster path is available via `data`");
2832            if (detailsEntryId && !detailsEntryId.source) {
2833                // Asking for completion details for an item that is not an auto-import
2834                return;
2835            }
2836
2837            flags |= CompletionInfoFlags.MayIncludeAutoImports;
2838            // import { type | -> token text should be blank
2839            const isAfterTypeOnlyImportSpecifierModifier = previousToken === contextToken
2840                && importStatementCompletion;
2841
2842            const lowerCaseTokenText =
2843                isAfterTypeOnlyImportSpecifierModifier ? "" :
2844                previousToken && isIdentifier(previousToken) ? previousToken.text.toLowerCase() :
2845                "";
2846
2847            const moduleSpecifierCache = host.getModuleSpecifierCache?.();
2848            const exportInfo = getExportInfoMap(sourceFile, host, program, preferences, cancellationToken);
2849            const packageJsonAutoImportProvider = host.getPackageJsonAutoImportProvider?.();
2850            const packageJsonFilter = detailsEntryId ? undefined : createPackageJsonImportFilter(sourceFile, preferences, host);
2851            resolvingModuleSpecifiers(
2852                "collectAutoImports",
2853                host,
2854                importSpecifierResolver ||= codefix.createImportSpecifierResolver(sourceFile, program, host, preferences),
2855                program,
2856                position,
2857                preferences,
2858                !!importStatementCompletion,
2859                isValidTypeOnlyAliasUseSite(location),
2860                context => {
2861                    exportInfo.search(
2862                        sourceFile.path,
2863                        /*preferCapitalized*/ isRightOfOpenTag,
2864                        (symbolName, targetFlags) => {
2865                            if (!isIdentifierText(symbolName, getEmitScriptTarget(host.getCompilationSettings()))) return false;
2866                            if (!detailsEntryId && isStringANonContextualKeyword(symbolName)) return false;
2867                            if (!isTypeOnlyLocation && !importStatementCompletion && !(targetFlags & SymbolFlags.Value)) return false;
2868                            if (isTypeOnlyLocation && !(targetFlags & (SymbolFlags.Module | SymbolFlags.Type))) return false;
2869                            // Do not try to auto-import something with a lowercase first letter for a JSX tag
2870                            const firstChar = symbolName.charCodeAt(0);
2871                            if (isRightOfOpenTag && (firstChar < CharacterCodes.A || firstChar > CharacterCodes.Z)) return false;
2872
2873                            if (detailsEntryId) return true;
2874                            return charactersFuzzyMatchInString(symbolName, lowerCaseTokenText);
2875                        },
2876                        (info, symbolName, isFromAmbientModule, exportMapKey) => {
2877                            if (detailsEntryId && !some(info, i => detailsEntryId.source === stripQuotes(i.moduleSymbol.name))) {
2878                                return;
2879                            }
2880
2881                            // Do a relatively cheap check to bail early if all re-exports are non-importable
2882                            // due to file location or package.json dependency filtering. For non-node16+
2883                            // module resolution modes, getting past this point guarantees that we'll be
2884                            // able to generate a suitable module specifier, so we can safely show a completion,
2885                            // even if we defer computing the module specifier.
2886                            const firstImportableExportInfo = find(info, isImportableExportInfo);
2887                            if (!firstImportableExportInfo) {
2888                                return;
2889                            }
2890
2891                            // In node16+, module specifier resolution can fail due to modules being blocked
2892                            // by package.json `exports`. If that happens, don't show a completion item.
2893                            // N.B. in this resolution mode we always try to resolve module specifiers here,
2894                            // because we have to know now if it's going to fail so we can omit the completion
2895                            // from the list.
2896                            const result = context.tryResolve(info, symbolName, isFromAmbientModule) || {};
2897                            if (result === "failed") return;
2898
2899                            // If we skipped resolving module specifiers, our selection of which ExportInfo
2900                            // to use here is arbitrary, since the info shown in the completion list derived from
2901                            // it should be identical regardless of which one is used. During the subsequent
2902                            // `CompletionEntryDetails` request, we'll get all the ExportInfos again and pick
2903                            // the best one based on the module specifier it produces.
2904                            let exportInfo = firstImportableExportInfo, moduleSpecifier;
2905                            if (result !== "skipped") {
2906                                ({ exportInfo = firstImportableExportInfo, moduleSpecifier } = result);
2907                            }
2908
2909                            const isDefaultExport = exportInfo.exportKind === ExportKind.Default;
2910                            const symbol = isDefaultExport && getLocalSymbolForExportDefault(exportInfo.symbol) || exportInfo.symbol;
2911
2912                            pushAutoImportSymbol(symbol, {
2913                                kind: moduleSpecifier ? SymbolOriginInfoKind.ResolvedExport : SymbolOriginInfoKind.Export,
2914                                moduleSpecifier,
2915                                symbolName,
2916                                exportMapKey,
2917                                exportName: exportInfo.exportKind === ExportKind.ExportEquals ? InternalSymbolName.ExportEquals : exportInfo.symbol.name,
2918                                fileName: exportInfo.moduleFileName,
2919                                isDefaultExport,
2920                                moduleSymbol: exportInfo.moduleSymbol,
2921                                isFromPackageJson: exportInfo.isFromPackageJson,
2922                            });
2923                        }
2924                    );
2925
2926                    hasUnresolvedAutoImports = context.skippedAny();
2927                    flags |= context.resolvedAny() ? CompletionInfoFlags.ResolvedModuleSpecifiers : 0;
2928                    flags |= context.resolvedBeyondLimit() ? CompletionInfoFlags.ResolvedModuleSpecifiersBeyondLimit : 0;
2929                }
2930            );
2931
2932            function isImportableExportInfo(info: SymbolExportInfo) {
2933                const moduleFile = tryCast(info.moduleSymbol.valueDeclaration, isSourceFile);
2934                if (!moduleFile) {
2935                    const moduleName = stripQuotes(info.moduleSymbol.name);
2936                    if (JsTyping.nodeCoreModules.has(moduleName) && startsWith(moduleName, "node:") !== shouldUseUriStyleNodeCoreModules(sourceFile, program)) {
2937                        return false;
2938                    }
2939                    return packageJsonFilter
2940                        ? packageJsonFilter.allowsImportingAmbientModule(info.moduleSymbol, getModuleSpecifierResolutionHost(info.isFromPackageJson))
2941                        : true;
2942                }
2943                return isImportableFile(
2944                    info.isFromPackageJson ? packageJsonAutoImportProvider! : program,
2945                    sourceFile,
2946                    moduleFile,
2947                    preferences,
2948                    packageJsonFilter,
2949                    getModuleSpecifierResolutionHost(info.isFromPackageJson),
2950                    moduleSpecifierCache);
2951            }
2952        }
2953
2954        function pushAutoImportSymbol(symbol: Symbol, origin: SymbolOriginInfoResolvedExport | SymbolOriginInfoExport) {
2955            const symbolId = getSymbolId(symbol);
2956            if (symbolToSortTextMap[symbolId] === SortText.GlobalsOrKeywords) {
2957                // If an auto-importable symbol is available as a global, don't add the auto import
2958                return;
2959            }
2960            symbolToOriginInfoMap[symbols.length] = origin;
2961            symbolToSortTextMap[symbolId] = importStatementCompletion ? SortText.LocationPriority : SortText.AutoImportSuggestions;
2962            symbols.push(symbol);
2963        }
2964
2965        /* Mutates `symbols` and `symbolToOriginInfoMap`. */
2966        function collectObjectLiteralMethodSymbols(members: Symbol[], enclosingDeclaration: ObjectLiteralExpression): void {
2967            // TODO: support JS files.
2968            if (isInJSFile(location)) {
2969                return;
2970            }
2971            members.forEach(member => {
2972                if (!isObjectLiteralMethodSymbol(member)) {
2973                    return;
2974                }
2975                const displayName = getCompletionEntryDisplayNameForSymbol(
2976                    member,
2977                    getEmitScriptTarget(compilerOptions),
2978                    /*origin*/ undefined,
2979                    CompletionKind.ObjectPropertyDeclaration,
2980                    /*jsxIdentifierExpected*/ false);
2981                if (!displayName) {
2982                    return;
2983                }
2984                const { name } = displayName;
2985                const entryProps = getEntryForObjectLiteralMethodCompletion(
2986                    member,
2987                    name,
2988                    enclosingDeclaration,
2989                    program,
2990                    host,
2991                    compilerOptions,
2992                    preferences,
2993                    formatContext);
2994                if (!entryProps) {
2995                    return;
2996                }
2997                const origin: SymbolOriginInfoObjectLiteralMethod = { kind: SymbolOriginInfoKind.ObjectLiteralMethod, ...entryProps };
2998                flags |= CompletionInfoFlags.MayIncludeMethodSnippets;
2999                symbolToOriginInfoMap[symbols.length] = origin;
3000                symbols.push(member);
3001            });
3002        }
3003
3004        function isObjectLiteralMethodSymbol(symbol: Symbol): boolean {
3005            /*
3006                For an object type
3007                `type Foo = {
3008                    bar(x: number): void;
3009                    foo: (x: string) => string;
3010                }`,
3011                `bar` will have symbol flag `Method`,
3012                `foo` will have symbol flag `Property`.
3013            */
3014            if (!(symbol.flags & (SymbolFlags.Property | SymbolFlags.Method))) {
3015                return false;
3016            }
3017            return true;
3018        }
3019
3020        /**
3021         * Finds the first node that "embraces" the position, so that one may
3022         * accurately aggregate locals from the closest containing scope.
3023         */
3024        function getScopeNode(initialToken: Node | undefined, position: number, sourceFile: SourceFile) {
3025            let scope: Node | undefined = initialToken;
3026            while (scope && !positionBelongsToNode(scope, position, sourceFile)) {
3027                scope = scope.parent;
3028            }
3029            return scope;
3030        }
3031
3032        function isCompletionListBlocker(contextToken: Node): boolean {
3033            const start = timestamp();
3034            const result = isInStringOrRegularExpressionOrTemplateLiteral(contextToken) ||
3035                isSolelyIdentifierDefinitionLocation(contextToken) ||
3036                isDotOfNumericLiteral(contextToken) ||
3037                isInJsxText(contextToken) ||
3038                isBigIntLiteral(contextToken);
3039            log("getCompletionsAtPosition: isCompletionListBlocker: " + (timestamp() - start));
3040            return result;
3041        }
3042
3043        function isInJsxText(contextToken: Node): boolean {
3044            if (contextToken.kind === SyntaxKind.JsxText) {
3045                return true;
3046            }
3047
3048            if (contextToken.kind === SyntaxKind.GreaterThanToken && contextToken.parent) {
3049                // <Component<string> /**/ />
3050                // <Component<string> /**/ ><Component>
3051                // - contextToken: GreaterThanToken (before cursor)
3052                // - location: JsxSelfClosingElement or JsxOpeningElement
3053                // - contextToken.parent === location
3054                if (location === contextToken.parent && (location.kind === SyntaxKind.JsxOpeningElement || location.kind === SyntaxKind.JsxSelfClosingElement)) {
3055                    return false;
3056                }
3057
3058                if (contextToken.parent.kind === SyntaxKind.JsxOpeningElement) {
3059                    // <div>/**/
3060                    // - contextToken: GreaterThanToken (before cursor)
3061                    // - location: JSXElement
3062                    // - different parents (JSXOpeningElement, JSXElement)
3063                    return location.parent.kind !== SyntaxKind.JsxOpeningElement;
3064                }
3065
3066                if (contextToken.parent.kind === SyntaxKind.JsxClosingElement || contextToken.parent.kind === SyntaxKind.JsxSelfClosingElement) {
3067                    return !!contextToken.parent.parent && contextToken.parent.parent.kind === SyntaxKind.JsxElement;
3068                }
3069            }
3070            return false;
3071        }
3072
3073        function isNewIdentifierDefinitionLocation(): boolean {
3074            if (contextToken) {
3075                const containingNodeKind = contextToken.parent.kind;
3076                const tokenKind = keywordForNode(contextToken);
3077                // Previous token may have been a keyword that was converted to an identifier.
3078                switch (tokenKind) {
3079                    case SyntaxKind.CommaToken:
3080                        return containingNodeKind === SyntaxKind.CallExpression               // func( a, |
3081                            || containingNodeKind === SyntaxKind.Constructor                  // constructor( a, |   /* public, protected, private keywords are allowed here, so show completion */
3082                            || containingNodeKind === SyntaxKind.NewExpression                // new C(a, |
3083                            || containingNodeKind === SyntaxKind.ArrayLiteralExpression       // [a, |
3084                            || containingNodeKind === SyntaxKind.BinaryExpression             // const x = (a, |
3085                            || containingNodeKind === SyntaxKind.FunctionType                 // var x: (s: string, list|
3086                            || containingNodeKind === SyntaxKind.ObjectLiteralExpression;     // const obj = { x, |
3087
3088                    case SyntaxKind.OpenParenToken:
3089                        return containingNodeKind === SyntaxKind.CallExpression               // func( |
3090                            || containingNodeKind === SyntaxKind.Constructor                  // constructor( |
3091                            || containingNodeKind === SyntaxKind.NewExpression                // new C(a|
3092                            || containingNodeKind === SyntaxKind.ParenthesizedExpression      // const x = (a|
3093                            || containingNodeKind === SyntaxKind.ParenthesizedType;           // function F(pred: (a| /* this can become an arrow function, where 'a' is the argument */
3094
3095                    case SyntaxKind.OpenBracketToken:
3096                        return containingNodeKind === SyntaxKind.ArrayLiteralExpression       // [ |
3097                            || containingNodeKind === SyntaxKind.IndexSignature               // [ | : string ]
3098                            || containingNodeKind === SyntaxKind.ComputedPropertyName;         // [ |    /* this can become an index signature */
3099
3100                    case SyntaxKind.ModuleKeyword:                                            // module |
3101                    case SyntaxKind.NamespaceKeyword:                                         // namespace |
3102                    case SyntaxKind.ImportKeyword:                                            // import |
3103                        return true;
3104
3105                    case SyntaxKind.DotToken:
3106                        return containingNodeKind === SyntaxKind.ModuleDeclaration;           // module A.|
3107
3108                    case SyntaxKind.OpenBraceToken:
3109                        return containingNodeKind === SyntaxKind.ClassDeclaration             // class A { |
3110                            || containingNodeKind === SyntaxKind.StructDeclaration            // struct A { |
3111                            || containingNodeKind === SyntaxKind.ObjectLiteralExpression;     // const obj = { |
3112
3113                    case SyntaxKind.EqualsToken:
3114                        return containingNodeKind === SyntaxKind.VariableDeclaration          // const x = a|
3115                            || containingNodeKind === SyntaxKind.BinaryExpression;            // x = a|
3116
3117                    case SyntaxKind.TemplateHead:
3118                        return containingNodeKind === SyntaxKind.TemplateExpression;          // `aa ${|
3119
3120                    case SyntaxKind.TemplateMiddle:
3121                        return containingNodeKind === SyntaxKind.TemplateSpan;                // `aa ${10} dd ${|
3122
3123                    case SyntaxKind.AsyncKeyword:
3124                        return containingNodeKind === SyntaxKind.MethodDeclaration            // const obj = { async c|()
3125                            || containingNodeKind === SyntaxKind.ShorthandPropertyAssignment; // const obj = { async c|
3126
3127                    case SyntaxKind.AsteriskToken:
3128                        return containingNodeKind === SyntaxKind.MethodDeclaration;           // const obj = { * c|
3129                }
3130
3131                if (isClassMemberCompletionKeyword(tokenKind)) {
3132                    return true;
3133                }
3134            }
3135
3136            return false;
3137        }
3138
3139        function isInStringOrRegularExpressionOrTemplateLiteral(contextToken: Node): boolean {
3140            // To be "in" one of these literals, the position has to be:
3141            //   1. entirely within the token text.
3142            //   2. at the end position of an unterminated token.
3143            //   3. at the end of a regular expression (due to trailing flags like '/foo/g').
3144            return (isRegularExpressionLiteral(contextToken) || isStringTextContainingNode(contextToken)) && (
3145                rangeContainsPositionExclusive(createTextRangeFromSpan(createTextSpanFromNode(contextToken)), position) ||
3146                position === contextToken.end && (!!contextToken.isUnterminated || isRegularExpressionLiteral(contextToken)));
3147        }
3148
3149        function tryGetObjectTypeLiteralInTypeArgumentCompletionSymbols(): GlobalsSearch | undefined {
3150            const typeLiteralNode = tryGetTypeLiteralNode(contextToken);
3151            if (!typeLiteralNode) return GlobalsSearch.Continue;
3152
3153            const intersectionTypeNode = isIntersectionTypeNode(typeLiteralNode.parent) ? typeLiteralNode.parent : undefined;
3154            const containerTypeNode = intersectionTypeNode || typeLiteralNode;
3155
3156            const containerExpectedType = getConstraintOfTypeArgumentProperty(containerTypeNode, typeChecker);
3157            if (!containerExpectedType) return GlobalsSearch.Continue;
3158
3159            const containerActualType = typeChecker.getTypeFromTypeNode(containerTypeNode);
3160
3161            const members = getPropertiesForCompletion(containerExpectedType, typeChecker);
3162            const existingMembers = getPropertiesForCompletion(containerActualType, typeChecker);
3163
3164            const existingMemberEscapedNames: Set<__String> = new Set();
3165            existingMembers.forEach(s => existingMemberEscapedNames.add(s.escapedName));
3166
3167            symbols = concatenate(symbols, filter(members, s => !existingMemberEscapedNames.has(s.escapedName)));
3168
3169            completionKind = CompletionKind.ObjectPropertyDeclaration;
3170            isNewIdentifierLocation = true;
3171
3172            return GlobalsSearch.Success;
3173        }
3174
3175        /**
3176         * Aggregates relevant symbols for completion in object literals and object binding patterns.
3177         * Relevant symbols are stored in the captured 'symbols' variable.
3178         *
3179         * @returns true if 'symbols' was successfully populated; false otherwise.
3180         */
3181        function tryGetObjectLikeCompletionSymbols(): GlobalsSearch | undefined {
3182            const symbolsStartIndex = symbols.length;
3183            const objectLikeContainer = tryGetObjectLikeCompletionContainer(contextToken);
3184            if (!objectLikeContainer) return GlobalsSearch.Continue;
3185
3186            // We're looking up possible property names from contextual/inferred/declared type.
3187            completionKind = CompletionKind.ObjectPropertyDeclaration;
3188
3189            let typeMembers: Symbol[] | undefined;
3190            let existingMembers: readonly Declaration[] | undefined;
3191
3192            if (objectLikeContainer.kind === SyntaxKind.ObjectLiteralExpression) {
3193                const instantiatedType = tryGetObjectLiteralContextualType(objectLikeContainer, typeChecker);
3194
3195                // Check completions for Object property value shorthand
3196                if (instantiatedType === undefined) {
3197                    if (objectLikeContainer.flags & NodeFlags.InWithStatement) {
3198                        return GlobalsSearch.Fail;
3199                    }
3200                    isNonContextualObjectLiteral = true;
3201                    return GlobalsSearch.Continue;
3202                }
3203                const completionsType = typeChecker.getContextualType(objectLikeContainer, ContextFlags.Completions);
3204                const hasStringIndexType = (completionsType || instantiatedType).getStringIndexType();
3205                const hasNumberIndextype = (completionsType || instantiatedType).getNumberIndexType();
3206                isNewIdentifierLocation = !!hasStringIndexType || !!hasNumberIndextype;
3207                typeMembers = getPropertiesForObjectExpression(instantiatedType, completionsType, objectLikeContainer, typeChecker);
3208                existingMembers = objectLikeContainer.properties;
3209
3210                if (typeMembers.length === 0) {
3211                    // Edge case: If NumberIndexType exists
3212                    if (!hasNumberIndextype) {
3213                        isNonContextualObjectLiteral = true;
3214                        return GlobalsSearch.Continue;
3215                    }
3216                }
3217            }
3218            else {
3219                Debug.assert(objectLikeContainer.kind === SyntaxKind.ObjectBindingPattern);
3220                // We are *only* completing on properties from the type being destructured.
3221                isNewIdentifierLocation = false;
3222
3223                const rootDeclaration = getRootDeclaration(objectLikeContainer.parent);
3224                if (!isVariableLike(rootDeclaration)) return Debug.fail("Root declaration is not variable-like.");
3225
3226                // We don't want to complete using the type acquired by the shape
3227                // of the binding pattern; we are only interested in types acquired
3228                // through type declaration or inference.
3229                // Also proceed if rootDeclaration is a parameter and if its containing function expression/arrow function is contextually typed -
3230                // type of parameter will flow in from the contextual type of the function
3231                let canGetType = hasInitializer(rootDeclaration) || !!getEffectiveTypeAnnotationNode(rootDeclaration) || rootDeclaration.parent.parent.kind === SyntaxKind.ForOfStatement;
3232                if (!canGetType && rootDeclaration.kind === SyntaxKind.Parameter) {
3233                    if (isExpression(rootDeclaration.parent)) {
3234                        canGetType = !!typeChecker.getContextualType(rootDeclaration.parent as Expression);
3235                    }
3236                    else if (rootDeclaration.parent.kind === SyntaxKind.MethodDeclaration || rootDeclaration.parent.kind === SyntaxKind.SetAccessor) {
3237                        canGetType = isExpression(rootDeclaration.parent.parent) && !!typeChecker.getContextualType(rootDeclaration.parent.parent as Expression);
3238                    }
3239                }
3240                if (canGetType) {
3241                    const typeForObject = typeChecker.getTypeAtLocation(objectLikeContainer);
3242                    if (!typeForObject) return GlobalsSearch.Fail;
3243                    typeMembers = typeChecker.getPropertiesOfType(typeForObject).filter(propertySymbol => {
3244                        return typeChecker.isPropertyAccessible(objectLikeContainer, /*isSuper*/ false, /*writing*/ false, typeForObject, propertySymbol);
3245                    });
3246                    existingMembers = objectLikeContainer.elements;
3247                }
3248            }
3249
3250            if (typeMembers && typeMembers.length > 0) {
3251                // Add filtered items to the completion list
3252                const filteredMembers = filterObjectMembersList(typeMembers, Debug.checkDefined(existingMembers));
3253                symbols = concatenate(symbols, filteredMembers);
3254                setSortTextToOptionalMember();
3255                if (objectLikeContainer.kind === SyntaxKind.ObjectLiteralExpression
3256                    && preferences.includeCompletionsWithObjectLiteralMethodSnippets
3257                    && preferences.includeCompletionsWithInsertText) {
3258                    transformObjectLiteralMembersSortText(symbolsStartIndex);
3259                    collectObjectLiteralMethodSymbols(filteredMembers, objectLikeContainer);
3260                }
3261            }
3262
3263            return GlobalsSearch.Success;
3264        }
3265
3266        /**
3267         * Aggregates relevant symbols for completion in import clauses and export clauses
3268         * whose declarations have a module specifier; for instance, symbols will be aggregated for
3269         *
3270         *      import { | } from "moduleName";
3271         *      export { a as foo, | } from "moduleName";
3272         *
3273         * but not for
3274         *
3275         *      export { | };
3276         *
3277         * Relevant symbols are stored in the captured 'symbols' variable.
3278         */
3279        function tryGetImportOrExportClauseCompletionSymbols(): GlobalsSearch {
3280            if (!contextToken) return GlobalsSearch.Continue;
3281
3282            // `import { |` or `import { a as 0, | }` or `import { type | }`
3283            const namedImportsOrExports =
3284                contextToken.kind === SyntaxKind.OpenBraceToken || contextToken.kind === SyntaxKind.CommaToken ? tryCast(contextToken.parent, isNamedImportsOrExports) :
3285                isTypeKeywordTokenOrIdentifier(contextToken) ? tryCast(contextToken.parent.parent, isNamedImportsOrExports) : undefined;
3286
3287            if (!namedImportsOrExports) return GlobalsSearch.Continue;
3288
3289            // We can at least offer `type` at `import { |`
3290            if (!isTypeKeywordTokenOrIdentifier(contextToken)) {
3291                keywordFilters = KeywordCompletionFilters.TypeKeyword;
3292            }
3293
3294            // try to show exported member for imported/re-exported module
3295            const { moduleSpecifier } = namedImportsOrExports.kind === SyntaxKind.NamedImports ? namedImportsOrExports.parent.parent : namedImportsOrExports.parent;
3296            if (!moduleSpecifier) {
3297                isNewIdentifierLocation = true;
3298                return namedImportsOrExports.kind === SyntaxKind.NamedImports ? GlobalsSearch.Fail : GlobalsSearch.Continue;
3299            }
3300            const moduleSpecifierSymbol = typeChecker.getSymbolAtLocation(moduleSpecifier); // TODO: GH#18217
3301            if (!moduleSpecifierSymbol) {
3302                isNewIdentifierLocation = true;
3303                return GlobalsSearch.Fail;
3304            }
3305
3306            completionKind = CompletionKind.MemberLike;
3307            isNewIdentifierLocation = false;
3308            const exports = typeChecker.getExportsAndPropertiesOfModule(moduleSpecifierSymbol);
3309            const existing = new Set((namedImportsOrExports.elements as NodeArray<ImportOrExportSpecifier>).filter(n => !isCurrentlyEditingNode(n)).map(n => (n.propertyName || n.name).escapedText));
3310            const uniques = exports.filter(e => e.escapedName !== InternalSymbolName.Default && !existing.has(e.escapedName));
3311            symbols = concatenate(symbols, uniques);
3312            if (!uniques.length) {
3313                // If there's nothing else to import, don't offer `type` either
3314                keywordFilters = KeywordCompletionFilters.None;
3315            }
3316            return GlobalsSearch.Success;
3317        }
3318
3319        /**
3320         * Adds local declarations for completions in named exports:
3321         *
3322         *   export { | };
3323         *
3324         * Does not check for the absence of a module specifier (`export {} from "./other"`)
3325         * because `tryGetImportOrExportClauseCompletionSymbols` runs first and handles that,
3326         * preventing this function from running.
3327         */
3328        function tryGetLocalNamedExportCompletionSymbols(): GlobalsSearch {
3329            const namedExports = contextToken && (contextToken.kind === SyntaxKind.OpenBraceToken || contextToken.kind === SyntaxKind.CommaToken)
3330                ? tryCast(contextToken.parent, isNamedExports)
3331                : undefined;
3332
3333            if (!namedExports) {
3334                return GlobalsSearch.Continue;
3335            }
3336
3337            const localsContainer = findAncestor(namedExports, or(isSourceFile, isModuleDeclaration))!;
3338            completionKind = CompletionKind.None;
3339            isNewIdentifierLocation = false;
3340            localsContainer.locals?.forEach((symbol, name) => {
3341                symbols.push(symbol);
3342                if (localsContainer.symbol?.exports?.has(name)) {
3343                    symbolToSortTextMap[getSymbolId(symbol)] = SortText.OptionalMember;
3344                }
3345            });
3346            return GlobalsSearch.Success;
3347        }
3348
3349        /**
3350         * Aggregates relevant symbols for completion in class declaration
3351         * Relevant symbols are stored in the captured 'symbols' variable.
3352         */
3353        function tryGetClassLikeCompletionSymbols(): GlobalsSearch {
3354            const decl = tryGetObjectTypeDeclarationCompletionContainer(sourceFile, contextToken, location, position);
3355            if (!decl) return GlobalsSearch.Continue;
3356
3357            // We're looking up possible property names from parent type.
3358            completionKind = CompletionKind.MemberLike;
3359            // Declaring new property/method/accessor
3360            isNewIdentifierLocation = true;
3361            keywordFilters = contextToken.kind === SyntaxKind.AsteriskToken ? KeywordCompletionFilters.None :
3362                isClassLike(decl) ? KeywordCompletionFilters.ClassElementKeywords : KeywordCompletionFilters.InterfaceElementKeywords;
3363
3364            // If you're in an interface you don't want to repeat things from super-interface. So just stop here.
3365            if (!isClassLike(decl)) return GlobalsSearch.Success;
3366
3367            const classElement = contextToken.kind === SyntaxKind.SemicolonToken ? contextToken.parent.parent : contextToken.parent;
3368            let classElementModifierFlags = isClassElement(classElement) ? getEffectiveModifierFlags(classElement) : ModifierFlags.None;
3369            // If this is context token is not something we are editing now, consider if this would lead to be modifier
3370            if (contextToken.kind === SyntaxKind.Identifier && !isCurrentlyEditingNode(contextToken)) {
3371                switch (contextToken.getText()) {
3372                    case "private":
3373                        classElementModifierFlags = classElementModifierFlags | ModifierFlags.Private;
3374                        break;
3375                    case "static":
3376                        classElementModifierFlags = classElementModifierFlags | ModifierFlags.Static;
3377                        break;
3378                    case "override":
3379                        classElementModifierFlags = classElementModifierFlags | ModifierFlags.Override;
3380                        break;
3381                }
3382            }
3383            if (isClassStaticBlockDeclaration(classElement)) {
3384                classElementModifierFlags |= ModifierFlags.Static;
3385            }
3386
3387            // No member list for private methods
3388            if (!(classElementModifierFlags & ModifierFlags.Private)) {
3389                // List of property symbols of base type that are not private and already implemented
3390                const baseTypeNodes = isClassLike(decl) && classElementModifierFlags & ModifierFlags.Override ? singleElementArray(getEffectiveBaseTypeNode(decl)) : getAllSuperTypeNodes(decl);
3391                const baseSymbols = flatMap(baseTypeNodes, baseTypeNode => {
3392                    const type = typeChecker.getTypeAtLocation(baseTypeNode);
3393                    return classElementModifierFlags & ModifierFlags.Static ?
3394                        type?.symbol && typeChecker.getPropertiesOfType(typeChecker.getTypeOfSymbolAtLocation(type.symbol, decl)) :
3395                        type && typeChecker.getPropertiesOfType(type);
3396                });
3397                symbols = concatenate(symbols, filterClassMembersList(baseSymbols, decl.members, classElementModifierFlags));
3398            }
3399
3400            return GlobalsSearch.Success;
3401        }
3402
3403        function isConstructorParameterCompletion(node: Node): boolean {
3404            return !!node.parent && isParameter(node.parent) && isConstructorDeclaration(node.parent.parent)
3405                && (isParameterPropertyModifier(node.kind) || isDeclarationName(node));
3406        }
3407
3408        /**
3409         * Returns the immediate owning class declaration of a context token,
3410         * on the condition that one exists and that the context implies completion should be given.
3411         */
3412        function tryGetConstructorLikeCompletionContainer(contextToken: Node): ConstructorDeclaration | undefined {
3413            if (contextToken) {
3414                const parent = contextToken.parent;
3415                switch (contextToken.kind) {
3416                    case SyntaxKind.OpenParenToken:
3417                    case SyntaxKind.CommaToken:
3418                        return isConstructorDeclaration(contextToken.parent) ? contextToken.parent : undefined;
3419
3420                    default:
3421                        if (isConstructorParameterCompletion(contextToken)) {
3422                            return parent.parent as ConstructorDeclaration;
3423                        }
3424                }
3425            }
3426            return undefined;
3427        }
3428
3429        function tryGetFunctionLikeBodyCompletionContainer(contextToken: Node): FunctionLikeDeclaration | undefined {
3430            if (contextToken) {
3431                let prev: Node;
3432                const container = findAncestor(contextToken.parent, (node: Node) => {
3433                    if (isClassLike(node)) {
3434                        return "quit";
3435                    }
3436                    if (isFunctionLikeDeclaration(node) && prev === node.body) {
3437                        return true;
3438                    }
3439                    prev = node;
3440                    return false;
3441                });
3442                return container && container as FunctionLikeDeclaration;
3443            }
3444        }
3445
3446        function tryGetContainingJsxElement(contextToken: Node): JsxOpeningLikeElement | undefined {
3447            if (contextToken) {
3448                const parent = contextToken.parent;
3449                switch (contextToken.kind) {
3450                    case SyntaxKind.GreaterThanToken: // End of a type argument list
3451                    case SyntaxKind.LessThanSlashToken:
3452                    case SyntaxKind.SlashToken:
3453                    case SyntaxKind.Identifier:
3454                    case SyntaxKind.PropertyAccessExpression:
3455                    case SyntaxKind.JsxAttributes:
3456                    case SyntaxKind.JsxAttribute:
3457                    case SyntaxKind.JsxSpreadAttribute:
3458                        if (parent && (parent.kind === SyntaxKind.JsxSelfClosingElement || parent.kind === SyntaxKind.JsxOpeningElement)) {
3459                            if (contextToken.kind === SyntaxKind.GreaterThanToken) {
3460                                const precedingToken = findPrecedingToken(contextToken.pos, sourceFile, /*startNode*/ undefined);
3461                                if (!(parent as JsxOpeningLikeElement).typeArguments || (precedingToken && precedingToken.kind === SyntaxKind.SlashToken)) break;
3462                            }
3463                            return parent as JsxOpeningLikeElement;
3464                        }
3465                        else if (parent.kind === SyntaxKind.JsxAttribute) {
3466                            // Currently we parse JsxOpeningLikeElement as:
3467                            //      JsxOpeningLikeElement
3468                            //          attributes: JsxAttributes
3469                            //             properties: NodeArray<JsxAttributeLike>
3470                            return parent.parent.parent as JsxOpeningLikeElement;
3471                        }
3472                        break;
3473
3474                    // The context token is the closing } or " of an attribute, which means
3475                    // its parent is a JsxExpression, whose parent is a JsxAttribute,
3476                    // whose parent is a JsxOpeningLikeElement
3477                    case SyntaxKind.StringLiteral:
3478                        if (parent && ((parent.kind === SyntaxKind.JsxAttribute) || (parent.kind === SyntaxKind.JsxSpreadAttribute))) {
3479                            // Currently we parse JsxOpeningLikeElement as:
3480                            //      JsxOpeningLikeElement
3481                            //          attributes: JsxAttributes
3482                            //             properties: NodeArray<JsxAttributeLike>
3483                            return parent.parent.parent as JsxOpeningLikeElement;
3484                        }
3485
3486                        break;
3487
3488                    case SyntaxKind.CloseBraceToken:
3489                        if (parent &&
3490                            parent.kind === SyntaxKind.JsxExpression &&
3491                            parent.parent && parent.parent.kind === SyntaxKind.JsxAttribute) {
3492                            // Currently we parse JsxOpeningLikeElement as:
3493                            //      JsxOpeningLikeElement
3494                            //          attributes: JsxAttributes
3495                            //             properties: NodeArray<JsxAttributeLike>
3496                            //                  each JsxAttribute can have initializer as JsxExpression
3497                            return parent.parent.parent.parent as JsxOpeningLikeElement;
3498                        }
3499
3500                        if (parent && parent.kind === SyntaxKind.JsxSpreadAttribute) {
3501                            // Currently we parse JsxOpeningLikeElement as:
3502                            //      JsxOpeningLikeElement
3503                            //          attributes: JsxAttributes
3504                            //             properties: NodeArray<JsxAttributeLike>
3505                            return parent.parent.parent as JsxOpeningLikeElement;
3506                        }
3507
3508                        break;
3509                }
3510            }
3511            return undefined;
3512        }
3513
3514        /**
3515         * @returns true if we are certain that the currently edited location must define a new location; false otherwise.
3516         */
3517        function isSolelyIdentifierDefinitionLocation(contextToken: Node): boolean {
3518            const parent = contextToken.parent;
3519            const containingNodeKind = parent.kind;
3520            switch (contextToken.kind) {
3521                case SyntaxKind.CommaToken:
3522                    return containingNodeKind === SyntaxKind.VariableDeclaration ||
3523                        isVariableDeclarationListButNotTypeArgument(contextToken) ||
3524                        containingNodeKind === SyntaxKind.VariableStatement ||
3525                        containingNodeKind === SyntaxKind.EnumDeclaration ||                        // enum a { foo, |
3526                        isFunctionLikeButNotConstructor(containingNodeKind) ||
3527                        containingNodeKind === SyntaxKind.InterfaceDeclaration ||                   // interface A<T, |
3528                        containingNodeKind === SyntaxKind.ArrayBindingPattern ||                    // var [x, y|
3529                        containingNodeKind === SyntaxKind.TypeAliasDeclaration ||                   // type Map, K, |
3530                        // class A<T, |
3531                        // var C = class D<T, |
3532                        (isClassLike(parent) &&
3533                            !!parent.typeParameters &&
3534                            parent.typeParameters.end >= contextToken.pos);
3535
3536                case SyntaxKind.DotToken:
3537                    return containingNodeKind === SyntaxKind.ArrayBindingPattern;                   // var [.|
3538
3539                case SyntaxKind.ColonToken:
3540                    return containingNodeKind === SyntaxKind.BindingElement;                        // var {x :html|
3541
3542                case SyntaxKind.OpenBracketToken:
3543                    return containingNodeKind === SyntaxKind.ArrayBindingPattern;                   // var [x|
3544
3545                case SyntaxKind.OpenParenToken:
3546                    return containingNodeKind === SyntaxKind.CatchClause ||
3547                        isFunctionLikeButNotConstructor(containingNodeKind);
3548
3549                case SyntaxKind.OpenBraceToken:
3550                    return containingNodeKind === SyntaxKind.EnumDeclaration;                       // enum a { |
3551
3552                case SyntaxKind.LessThanToken:
3553                    return containingNodeKind === SyntaxKind.ClassDeclaration ||                    // class A< |
3554                        containingNodeKind === SyntaxKind.ClassExpression ||                        // var C = class D< |
3555                        containingNodeKind === SyntaxKind.InterfaceDeclaration ||                   // interface A< |
3556                        containingNodeKind === SyntaxKind.TypeAliasDeclaration ||                   // type List< |
3557                        isFunctionLikeKind(containingNodeKind);
3558
3559                case SyntaxKind.StaticKeyword:
3560                    return containingNodeKind === SyntaxKind.PropertyDeclaration && !isClassLike(parent.parent);
3561
3562                case SyntaxKind.DotDotDotToken:
3563                    return containingNodeKind === SyntaxKind.Parameter ||
3564                        (!!parent.parent && parent.parent.kind === SyntaxKind.ArrayBindingPattern);  // var [...z|
3565
3566                case SyntaxKind.PublicKeyword:
3567                case SyntaxKind.PrivateKeyword:
3568                case SyntaxKind.ProtectedKeyword:
3569                    return containingNodeKind === SyntaxKind.Parameter && !isConstructorDeclaration(parent.parent);
3570
3571                case SyntaxKind.AsKeyword:
3572                    return containingNodeKind === SyntaxKind.ImportSpecifier ||
3573                        containingNodeKind === SyntaxKind.ExportSpecifier ||
3574                        containingNodeKind === SyntaxKind.NamespaceImport;
3575
3576                case SyntaxKind.GetKeyword:
3577                case SyntaxKind.SetKeyword:
3578                    return !isFromObjectTypeDeclaration(contextToken);
3579
3580                case SyntaxKind.Identifier:
3581                    if (containingNodeKind === SyntaxKind.ImportSpecifier &&
3582                        contextToken === (parent as ImportSpecifier).name &&
3583                        (contextToken as Identifier).text === "type"
3584                    ) {
3585                        // import { type | }
3586                        return false;
3587                    }
3588                    break;
3589
3590                case SyntaxKind.ClassKeyword:
3591                case SyntaxKind.StructKeyword:
3592                case SyntaxKind.EnumKeyword:
3593                case SyntaxKind.InterfaceKeyword:
3594                case SyntaxKind.FunctionKeyword:
3595                case SyntaxKind.VarKeyword:
3596                case SyntaxKind.ImportKeyword:
3597                case SyntaxKind.LetKeyword:
3598                case SyntaxKind.ConstKeyword:
3599                case SyntaxKind.InferKeyword:
3600                    return true;
3601
3602                case SyntaxKind.TypeKeyword:
3603                    // import { type foo| }
3604                    return containingNodeKind !== SyntaxKind.ImportSpecifier;
3605
3606                case SyntaxKind.AsteriskToken:
3607                    return isFunctionLike(contextToken.parent) && !isMethodDeclaration(contextToken.parent);
3608            }
3609
3610            // If the previous token is keyword corresponding to class member completion keyword
3611            // there will be completion available here
3612            if (isClassMemberCompletionKeyword(keywordForNode(contextToken)) && isFromObjectTypeDeclaration(contextToken)) {
3613                return false;
3614            }
3615
3616            if (isConstructorParameterCompletion(contextToken)) {
3617                // constructor parameter completion is available only if
3618                // - its modifier of the constructor parameter or
3619                // - its name of the parameter and not being edited
3620                // eg. constructor(a |<- this shouldnt show completion
3621                if (!isIdentifier(contextToken) ||
3622                    isParameterPropertyModifier(keywordForNode(contextToken)) ||
3623                    isCurrentlyEditingNode(contextToken)) {
3624                    return false;
3625                }
3626            }
3627
3628            // Previous token may have been a keyword that was converted to an identifier.
3629            switch (keywordForNode(contextToken)) {
3630                case SyntaxKind.AbstractKeyword:
3631                case SyntaxKind.ClassKeyword:
3632                case SyntaxKind.StructKeyword:
3633                case SyntaxKind.ConstKeyword:
3634                case SyntaxKind.DeclareKeyword:
3635                case SyntaxKind.EnumKeyword:
3636                case SyntaxKind.FunctionKeyword:
3637                case SyntaxKind.InterfaceKeyword:
3638                case SyntaxKind.LetKeyword:
3639                case SyntaxKind.PrivateKeyword:
3640                case SyntaxKind.ProtectedKeyword:
3641                case SyntaxKind.PublicKeyword:
3642                case SyntaxKind.StaticKeyword:
3643                case SyntaxKind.VarKeyword:
3644                    return true;
3645                case SyntaxKind.AsyncKeyword:
3646                    return isPropertyDeclaration(contextToken.parent);
3647            }
3648
3649            // If we are inside a class declaration, and `constructor` is totally not present,
3650            // but we request a completion manually at a whitespace...
3651            const ancestorClassLike = findAncestor(contextToken.parent, isClassLike);
3652            if (ancestorClassLike && contextToken === previousToken && isPreviousPropertyDeclarationTerminated(contextToken, position)) {
3653                return false; // Don't block completions.
3654            }
3655
3656            const ancestorPropertyDeclaraion = getAncestor(contextToken.parent, SyntaxKind.PropertyDeclaration);
3657            // If we are inside a class declaration and typing `constructor` after property declaration...
3658            if (ancestorPropertyDeclaraion
3659                && contextToken !== previousToken
3660                && isClassLike(previousToken.parent.parent)
3661                // And the cursor is at the token...
3662                && position <= previousToken.end) {
3663                // If we are sure that the previous property declaration is terminated according to newline or semicolon...
3664                if (isPreviousPropertyDeclarationTerminated(contextToken, previousToken.end)) {
3665                    return false; // Don't block completions.
3666                }
3667                else if (contextToken.kind !== SyntaxKind.EqualsToken
3668                    // Should not block: `class C { blah = c/**/ }`
3669                    // But should block: `class C { blah = somewhat c/**/ }` and `class C { blah: SomeType c/**/ }`
3670                    && (isInitializedProperty(ancestorPropertyDeclaraion as PropertyDeclaration)
3671                    || hasType(ancestorPropertyDeclaraion))) {
3672                    return true;
3673                }
3674            }
3675
3676            return isDeclarationName(contextToken)
3677                && !isShorthandPropertyAssignment(contextToken.parent)
3678                && !isJsxAttribute(contextToken.parent)
3679                // 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`.
3680                // If `contextToken !== previousToken`, this is `class C ex/**/`.
3681                && !(isClassLike(contextToken.parent) && (contextToken !== previousToken || position > previousToken.end));
3682        }
3683
3684        function isPreviousPropertyDeclarationTerminated(contextToken: Node, position: number) {
3685            return contextToken.kind !== SyntaxKind.EqualsToken &&
3686                (contextToken.kind === SyntaxKind.SemicolonToken
3687                || !positionsAreOnSameLine(contextToken.end, position, sourceFile));
3688        }
3689
3690        function isFunctionLikeButNotConstructor(kind: SyntaxKind) {
3691            return isFunctionLikeKind(kind) && kind !== SyntaxKind.Constructor;
3692        }
3693
3694        function isDotOfNumericLiteral(contextToken: Node): boolean {
3695            if (contextToken.kind === SyntaxKind.NumericLiteral) {
3696                const text = contextToken.getFullText();
3697                return text.charAt(text.length - 1) === ".";
3698            }
3699
3700            return false;
3701        }
3702
3703        function isVariableDeclarationListButNotTypeArgument(node: Node): boolean {
3704            return node.parent.kind === SyntaxKind.VariableDeclarationList
3705                && !isPossiblyTypeArgumentPosition(node, sourceFile, typeChecker);
3706        }
3707
3708        /**
3709         * Filters out completion suggestions for named imports or exports.
3710         *
3711         * @returns Symbols to be suggested in an object binding pattern or object literal expression, barring those whose declarations
3712         *          do not occur at the current position and have not otherwise been typed.
3713         */
3714        function filterObjectMembersList(contextualMemberSymbols: Symbol[], existingMembers: readonly Declaration[]): Symbol[] {
3715            if (existingMembers.length === 0) {
3716                return contextualMemberSymbols;
3717            }
3718
3719            const membersDeclaredBySpreadAssignment = new Set<string>();
3720            const existingMemberNames = new Set<__String>();
3721            for (const m of existingMembers) {
3722                // Ignore omitted expressions for missing members
3723                if (m.kind !== SyntaxKind.PropertyAssignment &&
3724                    m.kind !== SyntaxKind.ShorthandPropertyAssignment &&
3725                    m.kind !== SyntaxKind.BindingElement &&
3726                    m.kind !== SyntaxKind.MethodDeclaration &&
3727                    m.kind !== SyntaxKind.GetAccessor &&
3728                    m.kind !== SyntaxKind.SetAccessor &&
3729                    m.kind !== SyntaxKind.SpreadAssignment) {
3730                    continue;
3731                }
3732
3733                // If this is the current item we are editing right now, do not filter it out
3734                if (isCurrentlyEditingNode(m)) {
3735                    continue;
3736                }
3737
3738                let existingName: __String | undefined;
3739
3740                if (isSpreadAssignment(m)) {
3741                    setMembersDeclaredBySpreadAssignment(m, membersDeclaredBySpreadAssignment);
3742                }
3743                else if (isBindingElement(m) && m.propertyName) {
3744                    // include only identifiers in completion list
3745                    if (m.propertyName.kind === SyntaxKind.Identifier) {
3746                        existingName = m.propertyName.escapedText;
3747                    }
3748                }
3749                else {
3750                    // TODO: Account for computed property name
3751                    // NOTE: if one only performs this step when m.name is an identifier,
3752                    // things like '__proto__' are not filtered out.
3753                    const name = getNameOfDeclaration(m);
3754                    existingName = name && isPropertyNameLiteral(name) ? getEscapedTextOfIdentifierOrLiteral(name) : undefined;
3755                }
3756
3757                if (existingName !== undefined) {
3758                    existingMemberNames.add(existingName);
3759                }
3760            }
3761
3762            const filteredSymbols = contextualMemberSymbols.filter(m => !existingMemberNames.has(m.escapedName));
3763            setSortTextToMemberDeclaredBySpreadAssignment(membersDeclaredBySpreadAssignment, filteredSymbols);
3764
3765            return filteredSymbols;
3766        }
3767
3768        function setMembersDeclaredBySpreadAssignment(declaration: SpreadAssignment | JsxSpreadAttribute, membersDeclaredBySpreadAssignment: Set<string>) {
3769            const expression = declaration.expression;
3770            const symbol = typeChecker.getSymbolAtLocation(expression);
3771            const type = symbol && typeChecker.getTypeOfSymbolAtLocation(symbol, expression);
3772            const properties = type && (type as ObjectType).properties;
3773            if (properties) {
3774                properties.forEach(property => {
3775                    membersDeclaredBySpreadAssignment.add(property.name);
3776                });
3777            }
3778        }
3779
3780        // Set SortText to OptionalMember if it is an optional member
3781        function setSortTextToOptionalMember() {
3782            symbols.forEach(m => {
3783                if (m.flags & SymbolFlags.Optional) {
3784                    const symbolId = getSymbolId(m);
3785                    symbolToSortTextMap[symbolId] = symbolToSortTextMap[symbolId] ?? SortText.OptionalMember;
3786                }
3787            });
3788        }
3789
3790        // Set SortText to MemberDeclaredBySpreadAssignment if it is fulfilled by spread assignment
3791        function setSortTextToMemberDeclaredBySpreadAssignment(membersDeclaredBySpreadAssignment: Set<string>, contextualMemberSymbols: Symbol[]): void {
3792            if (membersDeclaredBySpreadAssignment.size === 0) {
3793                return;
3794            }
3795            for (const contextualMemberSymbol of contextualMemberSymbols) {
3796                if (membersDeclaredBySpreadAssignment.has(contextualMemberSymbol.name)) {
3797                    symbolToSortTextMap[getSymbolId(contextualMemberSymbol)] = SortText.MemberDeclaredBySpreadAssignment;
3798                }
3799            }
3800        }
3801
3802        function transformObjectLiteralMembersSortText(start: number): void {
3803            for (let i = start; i < symbols.length; i++) {
3804                const symbol = symbols[i];
3805                const symbolId = getSymbolId(symbol);
3806                const origin = symbolToOriginInfoMap?.[i];
3807                const target = getEmitScriptTarget(compilerOptions);
3808                const displayName = getCompletionEntryDisplayNameForSymbol(
3809                    symbol,
3810                    target,
3811                    origin,
3812                    CompletionKind.ObjectPropertyDeclaration,
3813                    /*jsxIdentifierExpected*/ false);
3814                if (displayName) {
3815                    const originalSortText = symbolToSortTextMap[symbolId] ?? SortText.LocationPriority;
3816                    const { name } = displayName;
3817                    symbolToSortTextMap[symbolId] = SortText.ObjectLiteralProperty(originalSortText, name);
3818                }
3819            }
3820        }
3821
3822        /**
3823         * Filters out completion suggestions for class elements.
3824         *
3825         * @returns Symbols to be suggested in an class element depending on existing memebers and symbol flags
3826         */
3827        function filterClassMembersList(baseSymbols: readonly Symbol[], existingMembers: readonly ClassElement[], currentClassElementModifierFlags: ModifierFlags): Symbol[] {
3828            const existingMemberNames = new Set<__String>();
3829            for (const m of existingMembers) {
3830                // Ignore omitted expressions for missing members
3831                if (m.kind !== SyntaxKind.PropertyDeclaration &&
3832                    m.kind !== SyntaxKind.MethodDeclaration &&
3833                    m.kind !== SyntaxKind.GetAccessor &&
3834                    m.kind !== SyntaxKind.SetAccessor) {
3835                    continue;
3836                }
3837
3838                // If this is the current item we are editing right now, do not filter it out
3839                if (isCurrentlyEditingNode(m)) {
3840                    continue;
3841                }
3842
3843                // Dont filter member even if the name matches if it is declared private in the list
3844                if (hasEffectiveModifier(m, ModifierFlags.Private)) {
3845                    continue;
3846                }
3847
3848                // do not filter it out if the static presence doesnt match
3849                if (isStatic(m) !== !!(currentClassElementModifierFlags & ModifierFlags.Static)) {
3850                    continue;
3851                }
3852
3853                const existingName = getPropertyNameForPropertyNameNode(m.name!);
3854                if (existingName) {
3855                    existingMemberNames.add(existingName);
3856                }
3857            }
3858
3859            return baseSymbols.filter(propertySymbol =>
3860                !existingMemberNames.has(propertySymbol.escapedName) &&
3861                !!propertySymbol.declarations &&
3862                !(getDeclarationModifierFlagsFromSymbol(propertySymbol) & ModifierFlags.Private) &&
3863                !(propertySymbol.valueDeclaration && isPrivateIdentifierClassElementDeclaration(propertySymbol.valueDeclaration)));
3864        }
3865
3866        /**
3867         * Filters out completion suggestions from 'symbols' according to existing JSX attributes.
3868         *
3869         * @returns Symbols to be suggested in a JSX element, barring those whose attributes
3870         *          do not occur at the current position and have not otherwise been typed.
3871         */
3872        function filterJsxAttributes(symbols: Symbol[], attributes: NodeArray<JsxAttribute | JsxSpreadAttribute>): Symbol[] {
3873            const seenNames = new Set<__String>();
3874            const membersDeclaredBySpreadAssignment = new Set<string>();
3875            for (const attr of attributes) {
3876                // If this is the current item we are editing right now, do not filter it out
3877                if (isCurrentlyEditingNode(attr)) {
3878                    continue;
3879                }
3880
3881                if (attr.kind === SyntaxKind.JsxAttribute) {
3882                    seenNames.add(attr.name.escapedText);
3883                }
3884                else if (isJsxSpreadAttribute(attr)) {
3885                    setMembersDeclaredBySpreadAssignment(attr, membersDeclaredBySpreadAssignment);
3886                }
3887            }
3888            const filteredSymbols = symbols.filter(a => !seenNames.has(a.escapedName));
3889
3890            setSortTextToMemberDeclaredBySpreadAssignment(membersDeclaredBySpreadAssignment, filteredSymbols);
3891
3892            return filteredSymbols;
3893        }
3894
3895        function isCurrentlyEditingNode(node: Node): boolean {
3896            return node.getStart(sourceFile) <= position && position <= node.getEnd();
3897        }
3898    }
3899
3900    /**
3901     * Returns the immediate owning object literal or binding pattern of a context token,
3902     * on the condition that one exists and that the context implies completion should be given.
3903     */
3904    function tryGetObjectLikeCompletionContainer(contextToken: Node | undefined): ObjectLiteralExpression | ObjectBindingPattern | undefined {
3905        if (contextToken) {
3906            const { parent } = contextToken;
3907            switch (contextToken.kind) {
3908                case SyntaxKind.OpenBraceToken:  // const x = { |
3909                case SyntaxKind.CommaToken:      // const x = { a: 0, |
3910                    if (isObjectLiteralExpression(parent) || isObjectBindingPattern(parent)) {
3911                        return parent;
3912                    }
3913                    break;
3914                case SyntaxKind.AsteriskToken:
3915                    return isMethodDeclaration(parent) ? tryCast(parent.parent, isObjectLiteralExpression) : undefined;
3916                case SyntaxKind.Identifier:
3917                    return (contextToken as Identifier).text === "async" && isShorthandPropertyAssignment(contextToken.parent)
3918                        ? contextToken.parent.parent : undefined;
3919            }
3920        }
3921
3922        return undefined;
3923    }
3924
3925    function getRelevantTokens(position: number, sourceFile: SourceFile): { contextToken: Node, previousToken: Node } | { contextToken: undefined, previousToken: undefined } {
3926        const previousToken = findPrecedingToken(position, sourceFile);
3927        if (previousToken && position <= previousToken.end && (isMemberName(previousToken) || isKeyword(previousToken.kind))) {
3928            const contextToken = findPrecedingToken(previousToken.getFullStart(), sourceFile, /*startNode*/ undefined)!; // TODO: GH#18217
3929            return { contextToken, previousToken };
3930        }
3931        return { contextToken: previousToken as Node, previousToken: previousToken as Node };
3932    }
3933
3934    function getAutoImportSymbolFromCompletionEntryData(name: string, data: CompletionEntryData, program: Program, host: LanguageServiceHost): { symbol: Symbol, origin: SymbolOriginInfoExport | SymbolOriginInfoResolvedExport } | undefined {
3935        const containingProgram = data.isPackageJsonImport ? host.getPackageJsonAutoImportProvider!()! : program;
3936        const checker = containingProgram.getTypeChecker();
3937        const moduleSymbol =
3938            data.ambientModuleName ? checker.tryFindAmbientModule(data.ambientModuleName) :
3939            data.fileName ? checker.getMergedSymbol(Debug.checkDefined(containingProgram.getSourceFile(data.fileName)).symbol) :
3940            undefined;
3941
3942        if (!moduleSymbol) return undefined;
3943        let symbol = data.exportName === InternalSymbolName.ExportEquals
3944            ? checker.resolveExternalModuleSymbol(moduleSymbol)
3945            : checker.tryGetMemberInModuleExportsAndProperties(data.exportName, moduleSymbol);
3946        if (!symbol) return undefined;
3947        const isDefaultExport = data.exportName === InternalSymbolName.Default;
3948        symbol = isDefaultExport && getLocalSymbolForExportDefault(symbol) || symbol;
3949        return { symbol, origin: completionEntryDataToSymbolOriginInfo(data, name, moduleSymbol) };
3950    }
3951
3952    interface CompletionEntryDisplayNameForSymbol {
3953        readonly name: string;
3954        readonly needsConvertPropertyAccess: boolean;
3955    }
3956    function getCompletionEntryDisplayNameForSymbol(
3957        symbol: Symbol,
3958        target: ScriptTarget,
3959        origin: SymbolOriginInfo | undefined,
3960        kind: CompletionKind,
3961        jsxIdentifierExpected: boolean,
3962    ): CompletionEntryDisplayNameForSymbol | undefined {
3963        const name = originIncludesSymbolName(origin) ? origin.symbolName : symbol.name;
3964        if (name === undefined
3965            // If the symbol is external module, don't show it in the completion list
3966            // (i.e declare module "http" { const x; } | // <= request completion here, "http" should not be there)
3967            || symbol.flags & SymbolFlags.Module && isSingleOrDoubleQuote(name.charCodeAt(0))
3968            // If the symbol is the internal name of an ES symbol, it is not a valid entry. Internal names for ES symbols start with "__@"
3969            || isKnownSymbol(symbol)) {
3970            return undefined;
3971        }
3972
3973        const validNameResult: CompletionEntryDisplayNameForSymbol = { name, needsConvertPropertyAccess: false };
3974        if (isIdentifierText(name, target, jsxIdentifierExpected ? LanguageVariant.JSX : LanguageVariant.Standard) || symbol.valueDeclaration && isPrivateIdentifierClassElementDeclaration(symbol.valueDeclaration)) {
3975            return validNameResult;
3976        }
3977        switch (kind) {
3978            case CompletionKind.MemberLike:
3979                return undefined;
3980            case CompletionKind.ObjectPropertyDeclaration:
3981                // TODO: GH#18169
3982                return { name: JSON.stringify(name), needsConvertPropertyAccess: false };
3983            case CompletionKind.PropertyAccess:
3984            case CompletionKind.Global: // For a 'this.' completion it will be in a global context, but may have a non-identifier name.
3985                // Don't add a completion for a name starting with a space. See https://github.com/Microsoft/TypeScript/pull/20547
3986                return name.charCodeAt(0) === CharacterCodes.space ? undefined : { name, needsConvertPropertyAccess: true };
3987            case CompletionKind.None:
3988            case CompletionKind.String:
3989                return validNameResult;
3990            default:
3991                Debug.assertNever(kind);
3992        }
3993    }
3994
3995    // A cache of completion entries for keywords, these do not change between sessions
3996    const _keywordCompletions: CompletionEntry[][] = [];
3997    const allKeywordsCompletions: () => readonly CompletionEntry[] = memoize(() => {
3998        const res: CompletionEntry[] = [];
3999        for (let i = SyntaxKind.FirstKeyword; i <= SyntaxKind.LastKeyword; i++) {
4000            res.push({
4001                name: tokenToString(i)!,
4002                kind: ScriptElementKind.keyword,
4003                kindModifiers: ScriptElementKindModifier.none,
4004                sortText: SortText.GlobalsOrKeywords
4005            });
4006        }
4007        return res;
4008    });
4009
4010    function getKeywordCompletions(keywordFilter: KeywordCompletionFilters, filterOutTsOnlyKeywords: boolean): readonly CompletionEntry[] {
4011        if (!filterOutTsOnlyKeywords) return getTypescriptKeywordCompletions(keywordFilter);
4012
4013        const index = keywordFilter + KeywordCompletionFilters.Last + 1;
4014        return _keywordCompletions[index] ||
4015            (_keywordCompletions[index] = getTypescriptKeywordCompletions(keywordFilter)
4016                .filter(entry => !isTypeScriptOnlyKeyword(stringToToken(entry.name)!))
4017            );
4018    }
4019
4020    function getTypescriptKeywordCompletions(keywordFilter: KeywordCompletionFilters): readonly CompletionEntry[] {
4021        return _keywordCompletions[keywordFilter] || (_keywordCompletions[keywordFilter] = allKeywordsCompletions().filter(entry => {
4022            const kind = stringToToken(entry.name)!;
4023            switch (keywordFilter) {
4024                case KeywordCompletionFilters.None:
4025                    return false;
4026                case KeywordCompletionFilters.All:
4027                    return isFunctionLikeBodyKeyword(kind)
4028                        || kind === SyntaxKind.DeclareKeyword
4029                        || kind === SyntaxKind.ModuleKeyword
4030                        || kind === SyntaxKind.TypeKeyword
4031                        || kind === SyntaxKind.NamespaceKeyword
4032                        || kind === SyntaxKind.AbstractKeyword
4033                        || isTypeKeyword(kind) && kind !== SyntaxKind.UndefinedKeyword;
4034                case KeywordCompletionFilters.FunctionLikeBodyKeywords:
4035                    return isFunctionLikeBodyKeyword(kind);
4036                case KeywordCompletionFilters.ClassElementKeywords:
4037                    return isClassMemberCompletionKeyword(kind);
4038                case KeywordCompletionFilters.InterfaceElementKeywords:
4039                    return isInterfaceOrTypeLiteralCompletionKeyword(kind);
4040                case KeywordCompletionFilters.ConstructorParameterKeywords:
4041                    return isParameterPropertyModifier(kind);
4042                case KeywordCompletionFilters.TypeAssertionKeywords:
4043                    return isTypeKeyword(kind) || kind === SyntaxKind.ConstKeyword;
4044                case KeywordCompletionFilters.TypeKeywords:
4045                    return isTypeKeyword(kind);
4046                case KeywordCompletionFilters.TypeKeyword:
4047                    return kind === SyntaxKind.TypeKeyword;
4048                default:
4049                    return Debug.assertNever(keywordFilter);
4050            }
4051        }));
4052    }
4053
4054    function isTypeScriptOnlyKeyword(kind: SyntaxKind) {
4055        switch (kind) {
4056            case SyntaxKind.AbstractKeyword:
4057            case SyntaxKind.AnyKeyword:
4058            case SyntaxKind.BigIntKeyword:
4059            case SyntaxKind.BooleanKeyword:
4060            case SyntaxKind.DeclareKeyword:
4061            case SyntaxKind.EnumKeyword:
4062            case SyntaxKind.GlobalKeyword:
4063            case SyntaxKind.ImplementsKeyword:
4064            case SyntaxKind.InferKeyword:
4065            case SyntaxKind.InterfaceKeyword:
4066            case SyntaxKind.IsKeyword:
4067            case SyntaxKind.KeyOfKeyword:
4068            case SyntaxKind.ModuleKeyword:
4069            case SyntaxKind.NamespaceKeyword:
4070            case SyntaxKind.NeverKeyword:
4071            case SyntaxKind.NumberKeyword:
4072            case SyntaxKind.ObjectKeyword:
4073            case SyntaxKind.OverrideKeyword:
4074            case SyntaxKind.PrivateKeyword:
4075            case SyntaxKind.ProtectedKeyword:
4076            case SyntaxKind.PublicKeyword:
4077            case SyntaxKind.ReadonlyKeyword:
4078            case SyntaxKind.StringKeyword:
4079            case SyntaxKind.SymbolKeyword:
4080            case SyntaxKind.TypeKeyword:
4081            case SyntaxKind.UniqueKeyword:
4082            case SyntaxKind.UnknownKeyword:
4083                return true;
4084            default:
4085                return false;
4086        }
4087    }
4088
4089    function isInterfaceOrTypeLiteralCompletionKeyword(kind: SyntaxKind): boolean {
4090        return kind === SyntaxKind.ReadonlyKeyword;
4091    }
4092
4093    function isClassMemberCompletionKeyword(kind: SyntaxKind) {
4094        switch (kind) {
4095            case SyntaxKind.AbstractKeyword:
4096            case SyntaxKind.AccessorKeyword:
4097            case SyntaxKind.ConstructorKeyword:
4098            case SyntaxKind.GetKeyword:
4099            case SyntaxKind.SetKeyword:
4100            case SyntaxKind.AsyncKeyword:
4101            case SyntaxKind.DeclareKeyword:
4102            case SyntaxKind.OverrideKeyword:
4103                return true;
4104            default:
4105                return isClassMemberModifier(kind);
4106        }
4107    }
4108
4109    function isFunctionLikeBodyKeyword(kind: SyntaxKind) {
4110        return kind === SyntaxKind.AsyncKeyword
4111            || kind === SyntaxKind.AwaitKeyword
4112            || kind === SyntaxKind.AsKeyword
4113            || kind === SyntaxKind.SatisfiesKeyword
4114            || kind === SyntaxKind.TypeKeyword
4115            || !isContextualKeyword(kind) && !isClassMemberCompletionKeyword(kind);
4116    }
4117
4118    function keywordForNode(node: Node): SyntaxKind {
4119        return isIdentifier(node) ? node.originalKeywordKind || SyntaxKind.Unknown : node.kind;
4120    }
4121
4122    function getContextualKeywords(
4123        contextToken: Node | undefined,
4124        position: number,
4125    ): readonly CompletionEntry[] {
4126        const entries = [];
4127        /**
4128         * An `AssertClause` can come after an import declaration:
4129         *  import * from "foo" |
4130         *  import "foo" |
4131         * or after a re-export declaration that has a module specifier:
4132         *  export { foo } from "foo" |
4133         * Source: https://tc39.es/proposal-import-assertions/
4134         */
4135        if (contextToken) {
4136            const file = contextToken.getSourceFile();
4137            const parent = contextToken.parent;
4138            const tokenLine = file.getLineAndCharacterOfPosition(contextToken.end).line;
4139            const currentLine = file.getLineAndCharacterOfPosition(position).line;
4140            if ((isImportDeclaration(parent) || isExportDeclaration(parent) && parent.moduleSpecifier)
4141                && contextToken === parent.moduleSpecifier
4142                && tokenLine === currentLine) {
4143                entries.push({
4144                    name: tokenToString(SyntaxKind.AssertKeyword)!,
4145                    kind: ScriptElementKind.keyword,
4146                    kindModifiers: ScriptElementKindModifier.none,
4147                    sortText: SortText.GlobalsOrKeywords,
4148                });
4149            }
4150        }
4151        return entries;
4152    }
4153
4154    /** Get the corresponding JSDocTag node if the position is in a jsDoc comment */
4155    function getJsDocTagAtPosition(node: Node, position: number): JSDocTag | undefined {
4156        return findAncestor(node, n =>
4157            isJSDocTag(n) && rangeContainsPosition(n, position) ? true :
4158                isJSDoc(n) ? "quit" : false) as JSDocTag | undefined;
4159    }
4160
4161    export function getPropertiesForObjectExpression(contextualType: Type, completionsType: Type | undefined, obj: ObjectLiteralExpression | JsxAttributes, checker: TypeChecker): Symbol[] {
4162        const hasCompletionsType = completionsType && completionsType !== contextualType;
4163        const type = hasCompletionsType && !(completionsType.flags & TypeFlags.AnyOrUnknown)
4164            ? checker.getUnionType([contextualType, completionsType])
4165            : contextualType;
4166
4167        const properties = getApparentProperties(type, obj, checker);
4168        return type.isClass() && containsNonPublicProperties(properties) ? [] :
4169            hasCompletionsType ? filter(properties, hasDeclarationOtherThanSelf) : properties;
4170
4171        // Filter out members whose only declaration is the object literal itself to avoid
4172        // self-fulfilling completions like:
4173        //
4174        // function f<T>(x: T) {}
4175        // f({ abc/**/: "" }) // `abc` is a member of `T` but only because it declares itself
4176        function hasDeclarationOtherThanSelf(member: Symbol) {
4177            if (!length(member.declarations)) return true;
4178            return some(member.declarations, decl => decl.parent !== obj);
4179        }
4180    }
4181
4182    function getApparentProperties(type: Type, node: ObjectLiteralExpression | JsxAttributes, checker: TypeChecker) {
4183        if (!type.isUnion()) return type.getApparentProperties();
4184        return checker.getAllPossiblePropertiesOfTypes(filter(type.types, memberType =>
4185            !(memberType.flags & TypeFlags.Primitive
4186                || checker.isArrayLikeType(memberType)
4187                || checker.isTypeInvalidDueToUnionDiscriminant(memberType, node)
4188                || typeHasCallOrConstructSignatures(memberType, checker)
4189                || memberType.isClass() && containsNonPublicProperties(memberType.getApparentProperties()))));
4190    }
4191
4192    function containsNonPublicProperties(props: Symbol[]) {
4193        return some(props, p => !!(getDeclarationModifierFlagsFromSymbol(p) & ModifierFlags.NonPublicAccessibilityModifier));
4194    }
4195
4196    /**
4197     * Gets all properties on a type, but if that type is a union of several types,
4198     * excludes array-like types or callable/constructable types.
4199     */
4200    function getPropertiesForCompletion(type: Type, checker: TypeChecker): Symbol[] {
4201        return type.isUnion()
4202            ? Debug.checkEachDefined(checker.getAllPossiblePropertiesOfTypes(type.types), "getAllPossiblePropertiesOfTypes() should all be defined")
4203            : Debug.checkEachDefined(type.getApparentProperties(), "getApparentProperties() should all be defined");
4204    }
4205
4206    /**
4207     * Returns the immediate owning class declaration of a context token,
4208     * on the condition that one exists and that the context implies completion should be given.
4209     */
4210    function tryGetObjectTypeDeclarationCompletionContainer(sourceFile: SourceFile, contextToken: Node | undefined, location: Node, position: number): ObjectTypeDeclaration | undefined {
4211        // class c { method() { } | method2() { } }
4212        switch (location.kind) {
4213            case SyntaxKind.SyntaxList:
4214                return tryCast(location.parent, isObjectTypeDeclaration);
4215            case SyntaxKind.EndOfFileToken:
4216                const cls = tryCast(lastOrUndefined(cast(location.parent, isSourceFile).statements), isObjectTypeDeclaration);
4217                if (cls && !findChildOfKind(cls, SyntaxKind.CloseBraceToken, sourceFile)) {
4218                    return cls;
4219                }
4220                break;
4221           case SyntaxKind.Identifier: {
4222                const originalKeywordKind = (location as Identifier).originalKeywordKind;
4223                if (originalKeywordKind && isKeyword(originalKeywordKind)) {
4224                    return undefined;
4225                }
4226                // class c { public prop = c| }
4227                if (isPropertyDeclaration(location.parent) && location.parent.initializer === location) {
4228                    return undefined;
4229                }
4230                // class c extends React.Component { a: () => 1\n compon| }
4231                if (isFromObjectTypeDeclaration(location)) {
4232                    return findAncestor(location, isObjectTypeDeclaration);
4233                }
4234            }
4235        }
4236
4237        if (!contextToken) return undefined;
4238
4239        // class C { blah; constructor/**/ } and so on
4240        if (location.kind === SyntaxKind.ConstructorKeyword
4241            // class C { blah \n constructor/**/ }
4242            || (isIdentifier(contextToken) && isPropertyDeclaration(contextToken.parent) && isClassLike(location))) {
4243            return findAncestor(contextToken, isClassLike) as ObjectTypeDeclaration;
4244        }
4245
4246        switch (contextToken.kind) {
4247            case SyntaxKind.EqualsToken: // class c { public prop = | /* global completions */ }
4248                return undefined;
4249
4250            case SyntaxKind.SemicolonToken: // class c {getValue(): number; | }
4251            case SyntaxKind.CloseBraceToken: // class c { method() { } | }
4252                // class c { method() { } b| }
4253                return isFromObjectTypeDeclaration(location) && (location.parent as ClassElement | TypeElement).name === location
4254                    ? location.parent.parent as ObjectTypeDeclaration
4255                    : tryCast(location, isObjectTypeDeclaration);
4256            case SyntaxKind.OpenBraceToken: // class c { |
4257            case SyntaxKind.CommaToken: // class c {getValue(): number, | }
4258                return tryCast(contextToken.parent, isObjectTypeDeclaration);
4259            default:
4260                if (!isFromObjectTypeDeclaration(contextToken)) {
4261                    // class c extends React.Component { a: () => 1\n| }
4262                    if (getLineAndCharacterOfPosition(sourceFile, contextToken.getEnd()).line !== getLineAndCharacterOfPosition(sourceFile, position).line && isObjectTypeDeclaration(location)) {
4263                        return location;
4264                    }
4265                    return undefined;
4266                }
4267                const isValidKeyword = isClassLike(contextToken.parent.parent) ? isClassMemberCompletionKeyword : isInterfaceOrTypeLiteralCompletionKeyword;
4268                return (isValidKeyword(contextToken.kind) || contextToken.kind === SyntaxKind.AsteriskToken || isIdentifier(contextToken) && isValidKeyword(stringToToken(contextToken.text)!)) // TODO: GH#18217
4269                    ? contextToken.parent.parent as ObjectTypeDeclaration : undefined;
4270        }
4271    }
4272
4273    function tryGetTypeLiteralNode(node: Node): TypeLiteralNode | undefined {
4274        if (!node) return undefined;
4275
4276        const parent = node.parent;
4277
4278        switch (node.kind) {
4279            case SyntaxKind.OpenBraceToken:
4280                if (isTypeLiteralNode(parent)) {
4281                    return parent;
4282                }
4283                break;
4284            case SyntaxKind.SemicolonToken:
4285            case SyntaxKind.CommaToken:
4286            case SyntaxKind.Identifier:
4287                if (parent.kind === SyntaxKind.PropertySignature && isTypeLiteralNode(parent.parent)) {
4288                    return parent.parent;
4289                }
4290                break;
4291        }
4292
4293        return undefined;
4294    }
4295
4296    function getConstraintOfTypeArgumentProperty(node: Node, checker: TypeChecker): Type | undefined {
4297        if (!node) return undefined;
4298
4299        if (isTypeNode(node) && isTypeReferenceType(node.parent)) {
4300            return checker.getTypeArgumentConstraint(node);
4301        }
4302
4303        const t = getConstraintOfTypeArgumentProperty(node.parent, checker);
4304        if (!t) return undefined;
4305
4306        switch (node.kind) {
4307            case SyntaxKind.PropertySignature:
4308                return checker.getTypeOfPropertyOfContextualType(t, node.symbol.escapedName);
4309            case SyntaxKind.IntersectionType:
4310            case SyntaxKind.TypeLiteral:
4311            case SyntaxKind.UnionType:
4312                return t;
4313        }
4314    }
4315
4316    // TODO: GH#19856 Would like to return `node is Node & { parent: (ClassElement | TypeElement) & { parent: ObjectTypeDeclaration } }` but then compilation takes > 10 minutes
4317    function isFromObjectTypeDeclaration(node: Node): boolean {
4318        return node.parent && isClassOrTypeElement(node.parent) && isObjectTypeDeclaration(node.parent.parent);
4319    }
4320
4321    function isValidTrigger(sourceFile: SourceFile, triggerCharacter: CompletionsTriggerCharacter, contextToken: Node | undefined, position: number): boolean {
4322        switch (triggerCharacter) {
4323            case ".":
4324            case "@":
4325                return true;
4326            case '"':
4327            case "'":
4328            case "`":
4329                // Only automatically bring up completions if this is an opening quote.
4330                return !!contextToken && isStringLiteralOrTemplate(contextToken) && position === contextToken.getStart(sourceFile) + 1;
4331            case "#":
4332                return !!contextToken && isPrivateIdentifier(contextToken) && !!getContainingClass(contextToken);
4333            case "<":
4334                // Opening JSX tag
4335                return !!contextToken && contextToken.kind === SyntaxKind.LessThanToken && (!isBinaryExpression(contextToken.parent) || binaryExpressionMayBeOpenTag(contextToken.parent));
4336            case "/":
4337                return !!contextToken && (isStringLiteralLike(contextToken)
4338                    ? !!tryGetImportFromModuleSpecifier(contextToken)
4339                    : contextToken.kind === SyntaxKind.SlashToken && isJsxClosingElement(contextToken.parent));
4340            case " ":
4341                return !!contextToken && isImportKeyword(contextToken) && contextToken.parent.kind === SyntaxKind.SourceFile;
4342            default:
4343                return Debug.assertNever(triggerCharacter);
4344        }
4345    }
4346
4347    function binaryExpressionMayBeOpenTag({ left }: BinaryExpression): boolean {
4348        return nodeIsMissing(left);
4349    }
4350
4351    /** Determines if a type is exactly the same type resolved by the global 'self', 'global', or 'globalThis'. */
4352    function isProbablyGlobalType(type: Type, sourceFile: SourceFile, checker: TypeChecker) {
4353        // The type of `self` and `window` is the same in lib.dom.d.ts, but `window` does not exist in
4354        // lib.webworker.d.ts, so checking against `self` is also a check against `window` when it exists.
4355        const selfSymbol = checker.resolveName("self", /*location*/ undefined, SymbolFlags.Value, /*excludeGlobals*/ false);
4356        if (selfSymbol && checker.getTypeOfSymbolAtLocation(selfSymbol, sourceFile) === type) {
4357            return true;
4358        }
4359        const globalSymbol = checker.resolveName("global", /*location*/ undefined, SymbolFlags.Value, /*excludeGlobals*/ false);
4360        if (globalSymbol && checker.getTypeOfSymbolAtLocation(globalSymbol, sourceFile) === type) {
4361            return true;
4362        }
4363        const globalThisSymbol = checker.resolveName("globalThis", /*location*/ undefined, SymbolFlags.Value, /*excludeGlobals*/ false);
4364        if (globalThisSymbol && checker.getTypeOfSymbolAtLocation(globalThisSymbol, sourceFile) === type) {
4365            return true;
4366        }
4367        return false;
4368    }
4369
4370    function isStaticProperty(symbol: Symbol) {
4371        return !!(symbol.valueDeclaration && getEffectiveModifierFlags(symbol.valueDeclaration) & ModifierFlags.Static && isClassLike(symbol.valueDeclaration.parent));
4372    }
4373
4374    function tryGetObjectLiteralContextualType(node: ObjectLiteralExpression, typeChecker: TypeChecker) {
4375        const type = typeChecker.getContextualType(node);
4376        if (type) {
4377            return type;
4378        }
4379        const parent = walkUpParenthesizedExpressions(node.parent);
4380        if (isBinaryExpression(parent) && parent.operatorToken.kind === SyntaxKind.EqualsToken && node === parent.left) {
4381            // Object literal is assignment pattern: ({ | } = x)
4382            return typeChecker.getTypeAtLocation(parent);
4383        }
4384        if (isExpression(parent)) {
4385            // f(() => (({ | })));
4386            return typeChecker.getContextualType(parent);
4387        }
4388        return undefined;
4389    }
4390
4391    interface ImportStatementCompletionInfo {
4392        isKeywordOnlyCompletion: boolean;
4393        keywordCompletion: TokenSyntaxKind | undefined;
4394        isNewIdentifierLocation: boolean;
4395        isTopLevelTypeOnly: boolean;
4396        couldBeTypeOnlyImportSpecifier: boolean;
4397        replacementSpan: TextSpan | undefined;
4398    }
4399
4400    function getImportStatementCompletionInfo(contextToken: Node): ImportStatementCompletionInfo {
4401        let keywordCompletion: TokenSyntaxKind | undefined;
4402        let isKeywordOnlyCompletion = false;
4403        const candidate = getCandidate();
4404        return {
4405            isKeywordOnlyCompletion,
4406            keywordCompletion,
4407            isNewIdentifierLocation: !!(candidate || keywordCompletion === SyntaxKind.TypeKeyword),
4408            isTopLevelTypeOnly: !!tryCast(candidate, isImportDeclaration)?.importClause?.isTypeOnly || !!tryCast(candidate, isImportEqualsDeclaration)?.isTypeOnly,
4409            couldBeTypeOnlyImportSpecifier: !!candidate && couldBeTypeOnlyImportSpecifier(candidate, contextToken),
4410            replacementSpan: getSingleLineReplacementSpanForImportCompletionNode(candidate),
4411        };
4412
4413        function getCandidate() {
4414            const parent = contextToken.parent;
4415            if (isImportEqualsDeclaration(parent)) {
4416                keywordCompletion = contextToken.kind === SyntaxKind.TypeKeyword ? undefined : SyntaxKind.TypeKeyword;
4417                return isModuleSpecifierMissingOrEmpty(parent.moduleReference) ? parent : undefined;
4418            }
4419            if (couldBeTypeOnlyImportSpecifier(parent, contextToken) && canCompleteFromNamedBindings(parent.parent)) {
4420                return parent;
4421            }
4422            if (isNamedImports(parent) || isNamespaceImport(parent)) {
4423                if (!parent.parent.isTypeOnly && (
4424                    contextToken.kind === SyntaxKind.OpenBraceToken ||
4425                    contextToken.kind === SyntaxKind.ImportKeyword ||
4426                    contextToken.kind === SyntaxKind.CommaToken
4427                )) {
4428                    keywordCompletion = SyntaxKind.TypeKeyword;
4429                }
4430
4431                if (canCompleteFromNamedBindings(parent)) {
4432                    // At `import { ... } |` or `import * as Foo |`, the only possible completion is `from`
4433                    if (contextToken.kind === SyntaxKind.CloseBraceToken || contextToken.kind === SyntaxKind.Identifier) {
4434                        isKeywordOnlyCompletion = true;
4435                        keywordCompletion = SyntaxKind.FromKeyword;
4436                    }
4437                    else {
4438                        return parent.parent.parent;
4439                    }
4440                }
4441                return undefined;
4442            }
4443            if (isImportKeyword(contextToken) && isSourceFile(parent)) {
4444                // A lone import keyword with nothing following it does not parse as a statement at all
4445                keywordCompletion = SyntaxKind.TypeKeyword;
4446                return contextToken as Token<SyntaxKind.ImportKeyword>;
4447            }
4448            if (isImportKeyword(contextToken) && isImportDeclaration(parent)) {
4449                // `import s| from`
4450                keywordCompletion = SyntaxKind.TypeKeyword;
4451                return isModuleSpecifierMissingOrEmpty(parent.moduleSpecifier) ? parent : undefined;
4452            }
4453            return undefined;
4454        }
4455    }
4456
4457    function getSingleLineReplacementSpanForImportCompletionNode(node: ImportDeclaration | ImportEqualsDeclaration | ImportSpecifier | Token<SyntaxKind.ImportKeyword> | undefined) {
4458        if (!node) return undefined;
4459        const top = findAncestor(node, or(isImportDeclaration, isImportEqualsDeclaration)) ?? node;
4460        const sourceFile = top.getSourceFile();
4461        if (rangeIsOnSingleLine(top, sourceFile)) {
4462            return createTextSpanFromNode(top, sourceFile);
4463        }
4464        // ImportKeyword was necessarily on one line; ImportSpecifier was necessarily parented in an ImportDeclaration
4465        Debug.assert(top.kind !== SyntaxKind.ImportKeyword && top.kind !== SyntaxKind.ImportSpecifier);
4466        // Guess which point in the import might actually be a later statement parsed as part of the import
4467        // during parser recovery - either in the middle of named imports, or the module specifier.
4468        const potentialSplitPoint = top.kind === SyntaxKind.ImportDeclaration
4469            ? getPotentiallyInvalidImportSpecifier(top.importClause?.namedBindings) ?? top.moduleSpecifier
4470            : top.moduleReference;
4471        const withoutModuleSpecifier: TextRange = {
4472            pos: top.getFirstToken()!.getStart(),
4473            end: potentialSplitPoint.pos,
4474        };
4475        // The module specifier/reference was previously found to be missing, empty, or
4476        // not a string literal - in this last case, it's likely that statement on a following
4477        // line was parsed as the module specifier of a partially-typed import, e.g.
4478        //   import Foo|
4479        //   interface Blah {}
4480        // This appears to be a multiline-import, and editors can't replace multiple lines.
4481        // But if everything but the "module specifier" is on one line, by this point we can
4482        // assume that the "module specifier" is actually just another statement, and return
4483        // the single-line range of the import excluding that probable statement.
4484        if (rangeIsOnSingleLine(withoutModuleSpecifier, sourceFile)) {
4485            return createTextSpanFromRange(withoutModuleSpecifier);
4486        }
4487    }
4488
4489    // Tries to identify the first named import that is not really a named import, but rather
4490    // just parser recovery for a situation like:
4491    //   import { Foo|
4492    //   interface Bar {}
4493    // in which `Foo`, `interface`, and `Bar` are all parsed as import specifiers. The caller
4494    // will also check if this token is on a separate line from the rest of the import.
4495    function getPotentiallyInvalidImportSpecifier(namedBindings: NamedImportBindings | undefined) {
4496        return find(
4497            tryCast(namedBindings, isNamedImports)?.elements,
4498            e => !e.propertyName &&
4499                isStringANonContextualKeyword(e.name.text) &&
4500                findPrecedingToken(e.name.pos, namedBindings!.getSourceFile(), namedBindings)?.kind !== SyntaxKind.CommaToken);
4501    }
4502
4503    function couldBeTypeOnlyImportSpecifier(importSpecifier: Node, contextToken: Node | undefined): importSpecifier is ImportSpecifier {
4504        return isImportSpecifier(importSpecifier)
4505            && (importSpecifier.isTypeOnly || contextToken === importSpecifier.name && isTypeKeywordTokenOrIdentifier(contextToken));
4506    }
4507
4508    function canCompleteFromNamedBindings(namedBindings: NamedImportBindings) {
4509        if (!isModuleSpecifierMissingOrEmpty(namedBindings.parent.parent.moduleSpecifier) || namedBindings.parent.name) {
4510            return false;
4511        }
4512        if (isNamedImports(namedBindings)) {
4513            // We can only complete on named imports if there are no other named imports already,
4514            // but parser recovery sometimes puts later statements in the named imports list, so
4515            // we try to only consider the probably-valid ones.
4516            const invalidNamedImport = getPotentiallyInvalidImportSpecifier(namedBindings);
4517            const validImports = invalidNamedImport ? namedBindings.elements.indexOf(invalidNamedImport) : namedBindings.elements.length;
4518            return validImports < 2;
4519        }
4520        return true;
4521    }
4522
4523    function isModuleSpecifierMissingOrEmpty(specifier: ModuleReference | Expression) {
4524        if (nodeIsMissing(specifier)) return true;
4525        return !tryCast(isExternalModuleReference(specifier) ? specifier.expression : specifier, isStringLiteralLike)?.text;
4526    }
4527
4528    function getVariableDeclaration(property: Node): VariableDeclaration | undefined {
4529        const variableDeclaration = findAncestor(property, node =>
4530            isFunctionBlock(node) || isArrowFunctionBody(node) || isBindingPattern(node)
4531                ? "quit"
4532                : isVariableDeclaration(node));
4533
4534        return variableDeclaration as VariableDeclaration | undefined;
4535    }
4536
4537    function isArrowFunctionBody(node: Node) {
4538        return node.parent && isArrowFunction(node.parent) && node.parent.body === node;
4539    }
4540
4541    /** True if symbol is a type or a module containing at least one type. */
4542    function symbolCanBeReferencedAtTypeLocation(symbol: Symbol, checker: TypeChecker, seenModules = new Map<SymbolId, true>()): boolean {
4543        // Since an alias can be merged with a local declaration, we need to test both the alias and its target.
4544        // This code used to just test the result of `skipAlias`, but that would ignore any locally introduced meanings.
4545        return nonAliasCanBeReferencedAtTypeLocation(symbol) || nonAliasCanBeReferencedAtTypeLocation(skipAlias(symbol.exportSymbol || symbol, checker));
4546
4547        function nonAliasCanBeReferencedAtTypeLocation(symbol: Symbol): boolean {
4548            return !!(symbol.flags & SymbolFlags.Type) || checker.isUnknownSymbol(symbol) ||
4549                !!(symbol.flags & SymbolFlags.Module) && addToSeen(seenModules, getSymbolId(symbol)) &&
4550                checker.getExportsOfModule(symbol).some(e => symbolCanBeReferencedAtTypeLocation(e, checker, seenModules));
4551        }
4552    }
4553
4554    function isDeprecated(symbol: Symbol, checker: TypeChecker) {
4555        const declarations = skipAlias(symbol, checker).declarations;
4556        return !!length(declarations) && every(declarations, isDeprecatedDeclaration);
4557    }
4558
4559    /**
4560     * True if the first character of `lowercaseCharacters` is the first character
4561     * of some "word" in `identiferString` (where the string is split into "words"
4562     * by camelCase and snake_case segments), then if the remaining characters of
4563     * `lowercaseCharacters` appear, in order, in the rest of `identifierString`.
4564     *
4565     * True:
4566     * 'state' in 'useState'
4567     * 'sae' in 'useState'
4568     * 'viable' in 'ENVIRONMENT_VARIABLE'
4569     *
4570     * False:
4571     * 'staet' in 'useState'
4572     * 'tate' in 'useState'
4573     * 'ment' in 'ENVIRONMENT_VARIABLE'
4574     */
4575     function charactersFuzzyMatchInString(identifierString: string, lowercaseCharacters: string): boolean {
4576        if (lowercaseCharacters.length === 0) {
4577            return true;
4578        }
4579
4580        let matchedFirstCharacter = false;
4581        let prevChar: number | undefined;
4582        let characterIndex = 0;
4583        const len = identifierString.length;
4584        for (let strIndex = 0; strIndex < len; strIndex++) {
4585            const strChar = identifierString.charCodeAt(strIndex);
4586            const testChar = lowercaseCharacters.charCodeAt(characterIndex);
4587            if (strChar === testChar || strChar === toUpperCharCode(testChar)) {
4588                matchedFirstCharacter ||=
4589                    prevChar === undefined || // Beginning of word
4590                    CharacterCodes.a <= prevChar && prevChar <= CharacterCodes.z && CharacterCodes.A <= strChar && strChar <= CharacterCodes.Z || // camelCase transition
4591                    prevChar === CharacterCodes._ && strChar !== CharacterCodes._; // snake_case transition
4592                if (matchedFirstCharacter) {
4593                    characterIndex++;
4594                }
4595                if (characterIndex === lowercaseCharacters.length) {
4596                    return true;
4597                }
4598            }
4599            prevChar = strChar;
4600        }
4601
4602        // Did not find all characters
4603        return false;
4604    }
4605
4606    function toUpperCharCode(charCode: number) {
4607        if (CharacterCodes.a <= charCode && charCode <= CharacterCodes.z) {
4608            return charCode - 32;
4609        }
4610        return charCode;
4611    }
4612
4613}
4614
4615