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