• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/* @internal */ // Don't expose that we use this
2// Based on lib.es6.d.ts
3interface PromiseConstructor {
4    new <T>(executor: (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void): Promise<T>;
5    reject(reason: any): Promise<never>;
6    all<T>(values: (T | PromiseLike<T>)[]): Promise<T[]>;
7}
8/* @internal */
9declare var Promise: PromiseConstructor; // eslint-disable-line no-var
10
11/* @internal */
12namespace ts {
13    // These utilities are common to multiple language service features.
14    //#region
15    export const scanner: Scanner = createScanner(ScriptTarget.Latest, /*skipTrivia*/ true);
16
17    export const enum SemanticMeaning {
18        None = 0x0,
19        Value = 0x1,
20        Type = 0x2,
21        Namespace = 0x4,
22        All = Value | Type | Namespace
23    }
24
25    export function getMeaningFromDeclaration(node: Node): SemanticMeaning {
26        switch (node.kind) {
27            case SyntaxKind.VariableDeclaration:
28                return isInJSFile(node) && getJSDocEnumTag(node) ? SemanticMeaning.All : SemanticMeaning.Value;
29
30            case SyntaxKind.Parameter:
31            case SyntaxKind.BindingElement:
32            case SyntaxKind.PropertyDeclaration:
33            case SyntaxKind.PropertySignature:
34            case SyntaxKind.PropertyAssignment:
35            case SyntaxKind.ShorthandPropertyAssignment:
36            case SyntaxKind.MethodDeclaration:
37            case SyntaxKind.MethodSignature:
38            case SyntaxKind.Constructor:
39            case SyntaxKind.GetAccessor:
40            case SyntaxKind.SetAccessor:
41            case SyntaxKind.FunctionDeclaration:
42            case SyntaxKind.FunctionExpression:
43            case SyntaxKind.ArrowFunction:
44            case SyntaxKind.CatchClause:
45            case SyntaxKind.JsxAttribute:
46                return SemanticMeaning.Value;
47
48            case SyntaxKind.TypeParameter:
49            case SyntaxKind.InterfaceDeclaration:
50            case SyntaxKind.TypeAliasDeclaration:
51            case SyntaxKind.TypeLiteral:
52                return SemanticMeaning.Type;
53
54            case SyntaxKind.JSDocTypedefTag:
55                // If it has no name node, it shares the name with the value declaration below it.
56                return (node as JSDocTypedefTag).name === undefined ? SemanticMeaning.Value | SemanticMeaning.Type : SemanticMeaning.Type;
57
58            case SyntaxKind.EnumMember:
59            case SyntaxKind.ClassDeclaration:
60            case SyntaxKind.StructDeclaration:
61                return SemanticMeaning.Value | SemanticMeaning.Type;
62
63            case SyntaxKind.ModuleDeclaration:
64                if (isAmbientModule(node as ModuleDeclaration)) {
65                    return SemanticMeaning.Namespace | SemanticMeaning.Value;
66                }
67                else if (getModuleInstanceState(node as ModuleDeclaration) === ModuleInstanceState.Instantiated) {
68                    return SemanticMeaning.Namespace | SemanticMeaning.Value;
69                }
70                else {
71                    return SemanticMeaning.Namespace;
72                }
73
74            case SyntaxKind.EnumDeclaration:
75            case SyntaxKind.NamedImports:
76            case SyntaxKind.ImportSpecifier:
77            case SyntaxKind.ImportEqualsDeclaration:
78            case SyntaxKind.ImportDeclaration:
79            case SyntaxKind.ExportAssignment:
80            case SyntaxKind.ExportDeclaration:
81                return SemanticMeaning.All;
82
83            // An external module can be a Value
84            case SyntaxKind.SourceFile:
85                return SemanticMeaning.Namespace | SemanticMeaning.Value;
86        }
87
88        return SemanticMeaning.All;
89    }
90
91    export function getMeaningFromLocation(node: Node): SemanticMeaning {
92        node = getAdjustedReferenceLocation(node);
93        const parent = node.parent;
94        if (node.kind === SyntaxKind.SourceFile) {
95            return SemanticMeaning.Value;
96        }
97        else if (isExportAssignment(parent)
98            || isExportSpecifier(parent)
99            || isExternalModuleReference(parent)
100            || isImportSpecifier(parent)
101            || isImportClause(parent)
102            || isImportEqualsDeclaration(parent) && node === parent.name) {
103            return SemanticMeaning.All;
104        }
105        else if (isInRightSideOfInternalImportEqualsDeclaration(node)) {
106            return getMeaningFromRightHandSideOfImportEquals(node as Identifier);
107        }
108        else if (isDeclarationName(node)) {
109            return getMeaningFromDeclaration(parent);
110        }
111        else if (isEntityName(node) && findAncestor(node, or(isJSDocNameReference, isJSDocLinkLike, isJSDocMemberName))) {
112            return SemanticMeaning.All;
113        }
114        else if (isTypeReference(node)) {
115            return SemanticMeaning.Type;
116        }
117        else if (isNamespaceReference(node)) {
118            return SemanticMeaning.Namespace;
119        }
120        else if (isTypeParameterDeclaration(parent)) {
121            Debug.assert(isJSDocTemplateTag(parent.parent)); // Else would be handled by isDeclarationName
122            return SemanticMeaning.Type;
123        }
124        else if (isLiteralTypeNode(parent)) {
125            // This might be T["name"], which is actually referencing a property and not a type. So allow both meanings.
126            return SemanticMeaning.Type | SemanticMeaning.Value;
127        }
128        else {
129            return SemanticMeaning.Value;
130        }
131    }
132
133    function getMeaningFromRightHandSideOfImportEquals(node: Node): SemanticMeaning {
134        //     import a = |b|; // Namespace
135        //     import a = |b.c|; // Value, type, namespace
136        //     import a = |b.c|.d; // Namespace
137        const name = node.kind === SyntaxKind.QualifiedName ? node : isQualifiedName(node.parent) && node.parent.right === node ? node.parent : undefined;
138        return name && name.parent.kind === SyntaxKind.ImportEqualsDeclaration ? SemanticMeaning.All : SemanticMeaning.Namespace;
139    }
140
141    export function isInRightSideOfInternalImportEqualsDeclaration(node: Node) {
142        while (node.parent.kind === SyntaxKind.QualifiedName) {
143            node = node.parent;
144        }
145        return isInternalModuleImportEqualsDeclaration(node.parent) && node.parent.moduleReference === node;
146    }
147
148    function isNamespaceReference(node: Node): boolean {
149        return isQualifiedNameNamespaceReference(node) || isPropertyAccessNamespaceReference(node);
150    }
151
152    function isQualifiedNameNamespaceReference(node: Node): boolean {
153        let root = node;
154        let isLastClause = true;
155        if (root.parent.kind === SyntaxKind.QualifiedName) {
156            while (root.parent && root.parent.kind === SyntaxKind.QualifiedName) {
157                root = root.parent;
158            }
159
160            isLastClause = (root as QualifiedName).right === node;
161        }
162
163        return root.parent.kind === SyntaxKind.TypeReference && !isLastClause;
164    }
165
166    function isPropertyAccessNamespaceReference(node: Node): boolean {
167        let root = node;
168        let isLastClause = true;
169        if (root.parent.kind === SyntaxKind.PropertyAccessExpression) {
170            while (root.parent && root.parent.kind === SyntaxKind.PropertyAccessExpression) {
171                root = root.parent;
172            }
173
174            isLastClause = (root as PropertyAccessExpression).name === node;
175        }
176
177        if (!isLastClause && root.parent.kind === SyntaxKind.ExpressionWithTypeArguments && root.parent.parent.kind === SyntaxKind.HeritageClause) {
178            const decl = root.parent.parent.parent;
179            return ((decl.kind === SyntaxKind.ClassDeclaration || decl.kind === SyntaxKind.StructDeclaration) && (root.parent.parent as HeritageClause).token === SyntaxKind.ImplementsKeyword) ||
180                (decl.kind === SyntaxKind.InterfaceDeclaration && (root.parent.parent as HeritageClause).token === SyntaxKind.ExtendsKeyword);
181        }
182
183        return false;
184    }
185
186    function isTypeReference(node: Node): boolean {
187        if (isRightSideOfQualifiedNameOrPropertyAccess(node)) {
188            node = node.parent;
189        }
190
191        switch (node.kind) {
192            case SyntaxKind.ThisKeyword:
193                return !isExpressionNode(node);
194            case SyntaxKind.ThisType:
195                return true;
196        }
197
198        switch (node.parent.kind) {
199            case SyntaxKind.TypeReference:
200                return true;
201            case SyntaxKind.ImportType:
202                return !(node.parent as ImportTypeNode).isTypeOf;
203            case SyntaxKind.ExpressionWithTypeArguments:
204                return isPartOfTypeNode(node.parent);
205        }
206
207        return false;
208    }
209
210    export function isCallExpressionTarget(node: Node, includeElementAccess = false, skipPastOuterExpressions = false): boolean {
211        return isCalleeWorker(node, isCallExpression, selectExpressionOfCallOrNewExpressionOrDecorator, includeElementAccess, skipPastOuterExpressions);
212    }
213
214    export function isNewExpressionTarget(node: Node, includeElementAccess = false, skipPastOuterExpressions = false): boolean {
215        return isCalleeWorker(node, isNewExpression, selectExpressionOfCallOrNewExpressionOrDecorator, includeElementAccess, skipPastOuterExpressions);
216    }
217
218    export function isCallOrNewExpressionTarget(node: Node, includeElementAccess = false, skipPastOuterExpressions = false): boolean {
219        return isCalleeWorker(node, isCallOrNewExpression, selectExpressionOfCallOrNewExpressionOrDecorator, includeElementAccess, skipPastOuterExpressions);
220    }
221
222    export function isTaggedTemplateTag(node: Node, includeElementAccess = false, skipPastOuterExpressions = false): boolean {
223        return isCalleeWorker(node, isTaggedTemplateExpression, selectTagOfTaggedTemplateExpression, includeElementAccess, skipPastOuterExpressions);
224    }
225
226    export function isDecoratorTarget(node: Node, includeElementAccess = false, skipPastOuterExpressions = false): boolean {
227        return isCalleeWorker(node, isDecorator, selectExpressionOfCallOrNewExpressionOrDecorator, includeElementAccess, skipPastOuterExpressions);
228    }
229
230    export function isJsxOpeningLikeElementTagName(node: Node, includeElementAccess = false, skipPastOuterExpressions = false): boolean {
231        return isCalleeWorker(node, isJsxOpeningLikeElement, selectTagNameOfJsxOpeningLikeElement, includeElementAccess, skipPastOuterExpressions);
232    }
233
234    function selectExpressionOfCallOrNewExpressionOrDecorator(node: CallExpression | NewExpression | Decorator) {
235        return node.expression;
236    }
237
238    function selectTagOfTaggedTemplateExpression(node: TaggedTemplateExpression) {
239        return node.tag;
240    }
241
242    function selectTagNameOfJsxOpeningLikeElement(node: JsxOpeningLikeElement) {
243        return node.tagName;
244    }
245
246    function isCalleeWorker<T extends CallExpression | NewExpression | TaggedTemplateExpression | Decorator | JsxOpeningLikeElement>(node: Node, pred: (node: Node) => node is T, calleeSelector: (node: T) => Expression, includeElementAccess: boolean, skipPastOuterExpressions: boolean) {
247        let target = includeElementAccess ? climbPastPropertyOrElementAccess(node) : climbPastPropertyAccess(node);
248        if (skipPastOuterExpressions) {
249            target = skipOuterExpressions(target);
250        }
251        return !!target && !!target.parent && pred(target.parent) && calleeSelector(target.parent) === target;
252    }
253
254    export function climbPastPropertyAccess(node: Node) {
255        return isRightSideOfPropertyAccess(node) ? node.parent : node;
256    }
257
258    export function climbPastPropertyOrElementAccess(node: Node) {
259        return isRightSideOfPropertyAccess(node) || isArgumentExpressionOfElementAccess(node) ? node.parent : node;
260    }
261
262    export function getTargetLabel(referenceNode: Node, labelName: string): Identifier | undefined {
263        while (referenceNode) {
264            if (referenceNode.kind === SyntaxKind.LabeledStatement && (referenceNode as LabeledStatement).label.escapedText === labelName) {
265                return (referenceNode as LabeledStatement).label;
266            }
267            referenceNode = referenceNode.parent;
268        }
269        return undefined;
270    }
271
272    export function hasPropertyAccessExpressionWithName(node: CallExpression, funcName: string): boolean {
273        if (!isPropertyAccessExpression(node.expression)) {
274            return false;
275        }
276
277        return node.expression.name.text === funcName;
278    }
279
280    export function isJumpStatementTarget(node: Node): node is Identifier & { parent: BreakOrContinueStatement } {
281        return isIdentifier(node) && tryCast(node.parent, isBreakOrContinueStatement)?.label === node;
282    }
283
284    export function isLabelOfLabeledStatement(node: Node): node is Identifier {
285        return isIdentifier(node) && tryCast(node.parent, isLabeledStatement)?.label === node;
286    }
287
288    export function isLabelName(node: Node): boolean {
289        return isLabelOfLabeledStatement(node) || isJumpStatementTarget(node);
290    }
291
292    export function isTagName(node: Node): boolean {
293        return tryCast(node.parent, isJSDocTag)?.tagName === node;
294    }
295
296    export function isRightSideOfQualifiedName(node: Node) {
297        return tryCast(node.parent, isQualifiedName)?.right === node;
298    }
299
300    export function isRightSideOfPropertyAccess(node: Node) {
301        return tryCast(node.parent, isPropertyAccessExpression)?.name === node;
302    }
303
304    export function isArgumentExpressionOfElementAccess(node: Node) {
305        return tryCast(node.parent, isElementAccessExpression)?.argumentExpression === node;
306    }
307
308    export function isNameOfModuleDeclaration(node: Node) {
309        return tryCast(node.parent, isModuleDeclaration)?.name === node;
310    }
311
312    export function isNameOfFunctionDeclaration(node: Node): boolean {
313        return isIdentifier(node) && tryCast(node.parent, isFunctionLike)?.name === node;
314    }
315
316    export function isLiteralNameOfPropertyDeclarationOrIndexAccess(node: StringLiteral | NumericLiteral | NoSubstitutionTemplateLiteral): boolean {
317        switch (node.parent.kind) {
318            case SyntaxKind.PropertyDeclaration:
319            case SyntaxKind.PropertySignature:
320            case SyntaxKind.PropertyAssignment:
321            case SyntaxKind.EnumMember:
322            case SyntaxKind.MethodDeclaration:
323            case SyntaxKind.MethodSignature:
324            case SyntaxKind.GetAccessor:
325            case SyntaxKind.SetAccessor:
326            case SyntaxKind.ModuleDeclaration:
327                return getNameOfDeclaration(node.parent as Declaration) === node;
328            case SyntaxKind.ElementAccessExpression:
329                return (node.parent as ElementAccessExpression).argumentExpression === node;
330            case SyntaxKind.ComputedPropertyName:
331                return true;
332            case SyntaxKind.LiteralType:
333                return node.parent.parent.kind === SyntaxKind.IndexedAccessType;
334            default:
335                return false;
336        }
337    }
338
339    export function isExpressionOfExternalModuleImportEqualsDeclaration(node: Node) {
340        return isExternalModuleImportEqualsDeclaration(node.parent.parent) &&
341            getExternalModuleImportEqualsDeclarationExpression(node.parent.parent) === node;
342    }
343
344    export function getContainerNode(node: Node): Declaration | undefined {
345        if (isJSDocTypeAlias(node)) {
346            // This doesn't just apply to the node immediately under the comment, but to everything in its parent's scope.
347            // node.parent = the JSDoc comment, node.parent.parent = the node having the comment.
348            // Then we get parent again in the loop.
349            node = node.parent.parent;
350        }
351
352        while (true) {
353            node = node.parent;
354            if (!node) {
355                return undefined;
356            }
357            switch (node.kind) {
358                case SyntaxKind.SourceFile:
359                case SyntaxKind.MethodDeclaration:
360                case SyntaxKind.MethodSignature:
361                case SyntaxKind.FunctionDeclaration:
362                case SyntaxKind.FunctionExpression:
363                case SyntaxKind.GetAccessor:
364                case SyntaxKind.SetAccessor:
365                case SyntaxKind.ClassDeclaration:
366                case SyntaxKind.StructDeclaration:
367                case SyntaxKind.InterfaceDeclaration:
368                case SyntaxKind.EnumDeclaration:
369                case SyntaxKind.ModuleDeclaration:
370                    return node as Declaration;
371            }
372        }
373    }
374
375    export function getNodeKind(node: Node): ScriptElementKind {
376        switch (node.kind) {
377            case SyntaxKind.SourceFile:
378                return isExternalModule(node as SourceFile) ? ScriptElementKind.moduleElement : ScriptElementKind.scriptElement;
379            case SyntaxKind.ModuleDeclaration:
380                return ScriptElementKind.moduleElement;
381            case SyntaxKind.ClassDeclaration:
382            case SyntaxKind.ClassExpression:
383                return ScriptElementKind.classElement;
384            case SyntaxKind.StructDeclaration:
385                return ScriptElementKind.structElement;
386            case SyntaxKind.InterfaceDeclaration: return ScriptElementKind.interfaceElement;
387            case SyntaxKind.TypeAliasDeclaration:
388            case SyntaxKind.JSDocCallbackTag:
389            case SyntaxKind.JSDocTypedefTag:
390                return ScriptElementKind.typeElement;
391            case SyntaxKind.EnumDeclaration: return ScriptElementKind.enumElement;
392            case SyntaxKind.VariableDeclaration:
393                return getKindOfVariableDeclaration(node as VariableDeclaration);
394            case SyntaxKind.BindingElement:
395                return getKindOfVariableDeclaration(getRootDeclaration(node) as VariableDeclaration);
396            case SyntaxKind.ArrowFunction:
397            case SyntaxKind.FunctionDeclaration:
398            case SyntaxKind.FunctionExpression:
399                return ScriptElementKind.functionElement;
400            case SyntaxKind.GetAccessor: return ScriptElementKind.memberGetAccessorElement;
401            case SyntaxKind.SetAccessor: return ScriptElementKind.memberSetAccessorElement;
402            case SyntaxKind.MethodDeclaration:
403            case SyntaxKind.MethodSignature:
404                return ScriptElementKind.memberFunctionElement;
405            case SyntaxKind.PropertyAssignment:
406                const { initializer } = node as PropertyAssignment;
407                return isFunctionLike(initializer) ? ScriptElementKind.memberFunctionElement : ScriptElementKind.memberVariableElement;
408            case SyntaxKind.PropertyDeclaration:
409            case SyntaxKind.PropertySignature:
410            case SyntaxKind.ShorthandPropertyAssignment:
411            case SyntaxKind.SpreadAssignment:
412                return ScriptElementKind.memberVariableElement;
413            case SyntaxKind.IndexSignature: return ScriptElementKind.indexSignatureElement;
414            case SyntaxKind.ConstructSignature: return ScriptElementKind.constructSignatureElement;
415            case SyntaxKind.CallSignature: return ScriptElementKind.callSignatureElement;
416            case SyntaxKind.Constructor:
417            case SyntaxKind.ClassStaticBlockDeclaration:
418                return ScriptElementKind.constructorImplementationElement;
419            case SyntaxKind.TypeParameter: return ScriptElementKind.typeParameterElement;
420            case SyntaxKind.EnumMember: return ScriptElementKind.enumMemberElement;
421            case SyntaxKind.Parameter: return hasSyntacticModifier(node, ModifierFlags.ParameterPropertyModifier) ? ScriptElementKind.memberVariableElement : ScriptElementKind.parameterElement;
422            case SyntaxKind.ImportEqualsDeclaration:
423            case SyntaxKind.ImportSpecifier:
424            case SyntaxKind.ExportSpecifier:
425            case SyntaxKind.NamespaceImport:
426            case SyntaxKind.NamespaceExport:
427                return ScriptElementKind.alias;
428            case SyntaxKind.BinaryExpression:
429                const kind = getAssignmentDeclarationKind(node as BinaryExpression);
430                const { right } = node as BinaryExpression;
431                switch (kind) {
432                    case AssignmentDeclarationKind.ObjectDefinePropertyValue:
433                    case AssignmentDeclarationKind.ObjectDefinePropertyExports:
434                    case AssignmentDeclarationKind.ObjectDefinePrototypeProperty:
435                    case AssignmentDeclarationKind.None:
436                        return ScriptElementKind.unknown;
437                    case AssignmentDeclarationKind.ExportsProperty:
438                    case AssignmentDeclarationKind.ModuleExports:
439                        const rightKind = getNodeKind(right);
440                        return rightKind === ScriptElementKind.unknown ? ScriptElementKind.constElement : rightKind;
441                    case AssignmentDeclarationKind.PrototypeProperty:
442                        return isFunctionExpression(right) ? ScriptElementKind.memberFunctionElement : ScriptElementKind.memberVariableElement;
443                    case AssignmentDeclarationKind.ThisProperty:
444                        return ScriptElementKind.memberVariableElement; // property
445                    case AssignmentDeclarationKind.Property:
446                        // static method / property
447                        return isFunctionExpression(right) ? ScriptElementKind.memberFunctionElement : ScriptElementKind.memberVariableElement;
448                    case AssignmentDeclarationKind.Prototype:
449                        return ScriptElementKind.localClassElement;
450                    default: {
451                        assertType<never>(kind);
452                        return ScriptElementKind.unknown;
453                    }
454                }
455            case SyntaxKind.Identifier:
456                return isImportClause(node.parent) ? ScriptElementKind.alias : ScriptElementKind.unknown;
457            case SyntaxKind.ExportAssignment:
458                const scriptKind = getNodeKind((node as ExportAssignment).expression);
459                // If the expression didn't come back with something (like it does for an identifiers)
460                return scriptKind === ScriptElementKind.unknown ? ScriptElementKind.constElement : scriptKind;
461            default:
462                return ScriptElementKind.unknown;
463        }
464
465        function getKindOfVariableDeclaration(v: VariableDeclaration): ScriptElementKind {
466            return isVarConst(v)
467                ? ScriptElementKind.constElement
468                : isLet(v)
469                    ? ScriptElementKind.letElement
470                    : ScriptElementKind.variableElement;
471        }
472    }
473
474    export function isThis(node: Node): boolean {
475        switch (node.kind) {
476            case SyntaxKind.ThisKeyword:
477                // case SyntaxKind.ThisType: TODO: GH#9267
478                return true;
479            case SyntaxKind.Identifier:
480                // 'this' as a parameter
481                return identifierIsThisKeyword(node as Identifier) && node.parent.kind === SyntaxKind.Parameter;
482            default:
483                return false;
484        }
485    }
486
487    // Matches the beginning of a triple slash directive
488    const tripleSlashDirectivePrefixRegex = /^\/\/\/\s*</;
489
490    export interface ListItemInfo {
491        listItemIndex: number;
492        list: Node;
493    }
494
495    export function getLineStartPositionForPosition(position: number, sourceFile: SourceFileLike): number {
496        const lineStarts = getLineStarts(sourceFile);
497        const line = sourceFile.getLineAndCharacterOfPosition(position).line;
498        return lineStarts[line];
499    }
500
501    export function rangeContainsRange(r1: TextRange, r2: TextRange): boolean {
502        return startEndContainsRange(r1.pos, r1.end, r2);
503    }
504
505    export function rangeContainsRangeExclusive(r1: TextRange, r2: TextRange): boolean {
506        return rangeContainsPositionExclusive(r1, r2.pos) && rangeContainsPositionExclusive(r1, r2.end);
507    }
508
509    export function rangeContainsPosition(r: TextRange, pos: number): boolean {
510        return r.pos <= pos && pos <= r.end;
511    }
512
513    export function rangeContainsPositionExclusive(r: TextRange, pos: number) {
514        return r.pos < pos && pos < r.end;
515    }
516
517    export function startEndContainsRange(start: number, end: number, range: TextRange): boolean {
518        return start <= range.pos && end >= range.end;
519    }
520
521    export function rangeContainsStartEnd(range: TextRange, start: number, end: number): boolean {
522        return range.pos <= start && range.end >= end;
523    }
524
525    export function rangeOverlapsWithStartEnd(r1: TextRange, start: number, end: number) {
526        return startEndOverlapsWithStartEnd(r1.pos, r1.end, start, end);
527    }
528
529    export function nodeOverlapsWithStartEnd(node: Node, sourceFile: SourceFile, start: number, end: number) {
530        return startEndOverlapsWithStartEnd(node.getStart(sourceFile), node.end, start, end);
531    }
532
533    export function startEndOverlapsWithStartEnd(start1: number, end1: number, start2: number, end2: number) {
534        const start = Math.max(start1, start2);
535        const end = Math.min(end1, end2);
536        return start < end;
537    }
538
539    /**
540     * Assumes `candidate.start <= position` holds.
541     */
542    export function positionBelongsToNode(candidate: Node, position: number, sourceFile: SourceFile): boolean {
543        Debug.assert(candidate.pos <= position);
544        return position < candidate.end || !isCompletedNode(candidate, sourceFile);
545    }
546
547    function isCompletedNode(n: Node | undefined, sourceFile: SourceFile): boolean {
548        if (n === undefined || nodeIsMissing(n)) {
549            return false;
550        }
551
552        switch (n.kind) {
553            case SyntaxKind.ClassDeclaration:
554            case SyntaxKind.StructDeclaration:
555            case SyntaxKind.InterfaceDeclaration:
556            case SyntaxKind.EnumDeclaration:
557            case SyntaxKind.ObjectLiteralExpression:
558            case SyntaxKind.ObjectBindingPattern:
559            case SyntaxKind.TypeLiteral:
560            case SyntaxKind.Block:
561            case SyntaxKind.ModuleBlock:
562            case SyntaxKind.CaseBlock:
563            case SyntaxKind.NamedImports:
564            case SyntaxKind.NamedExports:
565                return nodeEndsWith(n, SyntaxKind.CloseBraceToken, sourceFile);
566            case SyntaxKind.CatchClause:
567                return isCompletedNode((n as CatchClause).block, sourceFile);
568            case SyntaxKind.NewExpression:
569                if (!(n as NewExpression).arguments) {
570                    return true;
571                }
572            // falls through
573
574            case SyntaxKind.CallExpression:
575            case SyntaxKind.ParenthesizedExpression:
576            case SyntaxKind.ParenthesizedType:
577                return nodeEndsWith(n, SyntaxKind.CloseParenToken, sourceFile);
578
579            case SyntaxKind.FunctionType:
580            case SyntaxKind.ConstructorType:
581                return isCompletedNode((n as SignatureDeclaration).type, sourceFile);
582
583            case SyntaxKind.Constructor:
584            case SyntaxKind.GetAccessor:
585            case SyntaxKind.SetAccessor:
586            case SyntaxKind.FunctionDeclaration:
587            case SyntaxKind.FunctionExpression:
588            case SyntaxKind.MethodDeclaration:
589            case SyntaxKind.MethodSignature:
590            case SyntaxKind.ConstructSignature:
591            case SyntaxKind.CallSignature:
592            case SyntaxKind.ArrowFunction:
593                if ((n as FunctionLikeDeclaration).body) {
594                    return isCompletedNode((n as FunctionLikeDeclaration).body, sourceFile);
595                }
596
597                if ((n as FunctionLikeDeclaration).type) {
598                    return isCompletedNode((n as FunctionLikeDeclaration).type, sourceFile);
599                }
600
601                // Even though type parameters can be unclosed, we can get away with
602                // having at least a closing paren.
603                return hasChildOfKind(n, SyntaxKind.CloseParenToken, sourceFile);
604
605            case SyntaxKind.ModuleDeclaration:
606                return !!(n as ModuleDeclaration).body && isCompletedNode((n as ModuleDeclaration).body, sourceFile);
607
608            case SyntaxKind.IfStatement:
609                if ((n as IfStatement).elseStatement) {
610                    return isCompletedNode((n as IfStatement).elseStatement, sourceFile);
611                }
612                return isCompletedNode((n as IfStatement).thenStatement, sourceFile);
613
614            case SyntaxKind.ExpressionStatement:
615                return isCompletedNode((n as ExpressionStatement).expression, sourceFile) ||
616                    hasChildOfKind(n, SyntaxKind.SemicolonToken, sourceFile);
617
618            case SyntaxKind.ArrayLiteralExpression:
619            case SyntaxKind.ArrayBindingPattern:
620            case SyntaxKind.ElementAccessExpression:
621            case SyntaxKind.ComputedPropertyName:
622            case SyntaxKind.TupleType:
623                return nodeEndsWith(n, SyntaxKind.CloseBracketToken, sourceFile);
624
625            case SyntaxKind.IndexSignature:
626                if ((n as IndexSignatureDeclaration).type) {
627                    return isCompletedNode((n as IndexSignatureDeclaration).type, sourceFile);
628                }
629
630                return hasChildOfKind(n, SyntaxKind.CloseBracketToken, sourceFile);
631
632            case SyntaxKind.CaseClause:
633            case SyntaxKind.DefaultClause:
634                // there is no such thing as terminator token for CaseClause/DefaultClause so for simplicity always consider them non-completed
635                return false;
636
637            case SyntaxKind.ForStatement:
638            case SyntaxKind.ForInStatement:
639            case SyntaxKind.ForOfStatement:
640            case SyntaxKind.WhileStatement:
641                return isCompletedNode((n as IterationStatement).statement, sourceFile);
642            case SyntaxKind.DoStatement:
643                // rough approximation: if DoStatement has While keyword - then if node is completed is checking the presence of ')';
644                return hasChildOfKind(n, SyntaxKind.WhileKeyword, sourceFile)
645                    ? nodeEndsWith(n, SyntaxKind.CloseParenToken, sourceFile)
646                    : isCompletedNode((n as DoStatement).statement, sourceFile);
647
648            case SyntaxKind.TypeQuery:
649                return isCompletedNode((n as TypeQueryNode).exprName, sourceFile);
650
651            case SyntaxKind.TypeOfExpression:
652            case SyntaxKind.DeleteExpression:
653            case SyntaxKind.VoidExpression:
654            case SyntaxKind.YieldExpression:
655            case SyntaxKind.SpreadElement:
656                const unaryWordExpression = n as (TypeOfExpression | DeleteExpression | VoidExpression | YieldExpression | SpreadElement);
657                return isCompletedNode(unaryWordExpression.expression, sourceFile);
658
659            case SyntaxKind.TaggedTemplateExpression:
660                return isCompletedNode((n as TaggedTemplateExpression).template, sourceFile);
661            case SyntaxKind.TemplateExpression:
662                const lastSpan = lastOrUndefined((n as TemplateExpression).templateSpans);
663                return isCompletedNode(lastSpan, sourceFile);
664            case SyntaxKind.TemplateSpan:
665                return nodeIsPresent((n as TemplateSpan).literal);
666
667            case SyntaxKind.ExportDeclaration:
668            case SyntaxKind.ImportDeclaration:
669                return nodeIsPresent((n as ExportDeclaration | ImportDeclaration).moduleSpecifier);
670
671            case SyntaxKind.PrefixUnaryExpression:
672                return isCompletedNode((n as PrefixUnaryExpression).operand, sourceFile);
673            case SyntaxKind.BinaryExpression:
674                return isCompletedNode((n as BinaryExpression).right, sourceFile);
675            case SyntaxKind.ConditionalExpression:
676                return isCompletedNode((n as ConditionalExpression).whenFalse, sourceFile);
677
678            default:
679                return true;
680        }
681    }
682
683    /*
684     * Checks if node ends with 'expectedLastToken'.
685     * If child at position 'length - 1' is 'SemicolonToken' it is skipped and 'expectedLastToken' is compared with child at position 'length - 2'.
686     */
687    function nodeEndsWith(n: Node, expectedLastToken: SyntaxKind, sourceFile: SourceFile): boolean {
688        const children = n.getChildren(sourceFile);
689        if (children.length) {
690            const lastChild = last(children);
691            if (lastChild.kind === expectedLastToken) {
692                return true;
693            }
694            else if (lastChild.kind === SyntaxKind.SemicolonToken && children.length !== 1) {
695                return children[children.length - 2].kind === expectedLastToken;
696            }
697        }
698        return false;
699    }
700
701    export function findListItemInfo(node: Node): ListItemInfo | undefined {
702        const list = findContainingList(node);
703
704        // It is possible at this point for syntaxList to be undefined, either if
705        // node.parent had no list child, or if none of its list children contained
706        // the span of node. If this happens, return undefined. The caller should
707        // handle this case.
708        if (!list) {
709            return undefined;
710        }
711
712        const children = list.getChildren();
713        const listItemIndex = indexOfNode(children, node);
714
715        return {
716            listItemIndex,
717            list
718        };
719    }
720
721    export function hasChildOfKind(n: Node, kind: SyntaxKind, sourceFile: SourceFile): boolean {
722        return !!findChildOfKind(n, kind, sourceFile);
723    }
724
725    export function findChildOfKind<T extends Node>(n: Node, kind: T["kind"], sourceFile: SourceFileLike): T | undefined {
726        return find(n.getChildren(sourceFile), (c): c is T => c.kind === kind);
727    }
728
729    export function findContainingList(node: Node): SyntaxList | undefined {
730        // The node might be a list element (nonsynthetic) or a comma (synthetic). Either way, it will
731        // be parented by the container of the SyntaxList, not the SyntaxList itself.
732        // In order to find the list item index, we first need to locate SyntaxList itself and then search
733        // for the position of the relevant node (or comma).
734        const syntaxList = find(node.parent.getChildren(), (c): c is SyntaxList => isSyntaxList(c) && rangeContainsRange(c, node));
735        // Either we didn't find an appropriate list, or the list must contain us.
736        Debug.assert(!syntaxList || contains(syntaxList.getChildren(), node));
737        return syntaxList;
738    }
739
740    function isDefaultModifier(node: Node) {
741        return node.kind === SyntaxKind.DefaultKeyword;
742    }
743
744    function isClassKeyword(node: Node) {
745        return node.kind === SyntaxKind.ClassKeyword;
746    }
747
748    function isFunctionKeyword(node: Node) {
749        return node.kind === SyntaxKind.FunctionKeyword;
750    }
751
752    function getAdjustedLocationForClass(node: ClassDeclaration | ClassExpression | StructDeclaration) {
753        if (isNamedDeclaration(node)) {
754            return node.name;
755        }
756        if (isClassDeclaration(node) || isStructDeclaration(node)) {
757            // for class and function declarations, use the `default` modifier
758            // when the declaration is unnamed.
759            const defaultModifier = node.modifiers && find(node.modifiers, isDefaultModifier);
760            if (defaultModifier) return defaultModifier;
761        }
762        if (isClassExpression(node)) {
763            // for class expressions, use the `class` keyword when the class is unnamed
764            const classKeyword = find(node.getChildren(), isClassKeyword);
765            if (classKeyword) return classKeyword;
766        }
767    }
768
769    function getAdjustedLocationForFunction(node: FunctionDeclaration | FunctionExpression) {
770        if (isNamedDeclaration(node)) {
771            return node.name;
772        }
773        if (isFunctionDeclaration(node)) {
774            // for class and function declarations, use the `default` modifier
775            // when the declaration is unnamed.
776            const defaultModifier = find(node.modifiers, isDefaultModifier);
777            if (defaultModifier) return defaultModifier;
778        }
779        if (isFunctionExpression(node)) {
780            // for function expressions, use the `function` keyword when the function is unnamed
781            const functionKeyword = find(node.getChildren(), isFunctionKeyword);
782            if (functionKeyword) return functionKeyword;
783        }
784    }
785
786    function getAncestorTypeNode(node: Node) {
787        let lastTypeNode: TypeNode | undefined;
788        findAncestor(node, a => {
789            if (isTypeNode(a)) {
790                lastTypeNode = a;
791            }
792            return !isQualifiedName(a.parent) && !isTypeNode(a.parent) && !isTypeElement(a.parent);
793        });
794        return lastTypeNode;
795    }
796
797    export function getContextualTypeFromParentOrAncestorTypeNode(node: Expression, checker: TypeChecker): Type | undefined {
798        if (node.flags & (NodeFlags.JSDoc & ~NodeFlags.JavaScriptFile)) return undefined;
799
800        const contextualType = getContextualTypeFromParent(node, checker);
801        if (contextualType) return contextualType;
802
803        const ancestorTypeNode = getAncestorTypeNode(node);
804        return ancestorTypeNode && checker.getTypeAtLocation(ancestorTypeNode);
805    }
806
807    function getAdjustedLocationForDeclaration(node: Node, forRename: boolean) {
808        if (!forRename) {
809            switch (node.kind) {
810                case SyntaxKind.ClassDeclaration:
811                case SyntaxKind.ClassExpression:
812                case SyntaxKind.StructDeclaration:
813                    return getAdjustedLocationForClass(node as ClassDeclaration | ClassExpression | StructDeclaration);
814                case SyntaxKind.FunctionDeclaration:
815                case SyntaxKind.FunctionExpression:
816                    return getAdjustedLocationForFunction(node as FunctionDeclaration | FunctionExpression);
817                case SyntaxKind.Constructor:
818                    return node;
819            }
820        }
821        if (isNamedDeclaration(node)) {
822            return node.name;
823        }
824    }
825
826    function getAdjustedLocationForImportDeclaration(node: ImportDeclaration, forRename: boolean) {
827        if (node.importClause) {
828            if (node.importClause.name && node.importClause.namedBindings) {
829                // do not adjust if we have both a name and named bindings
830                return;
831            }
832
833            // /**/import [|name|] from ...;
834            // import /**/type [|name|] from ...;
835            if (node.importClause.name) {
836                return node.importClause.name;
837            }
838
839            // /**/import { [|name|] } from ...;
840            // /**/import { propertyName as [|name|] } from ...;
841            // /**/import * as [|name|] from ...;
842            // import /**/type { [|name|] } from ...;
843            // import /**/type { propertyName as [|name|] } from ...;
844            // import /**/type * as [|name|] from ...;
845            if (node.importClause.namedBindings) {
846                if (isNamedImports(node.importClause.namedBindings)) {
847                    // do nothing if there is more than one binding
848                    const onlyBinding = singleOrUndefined(node.importClause.namedBindings.elements);
849                    if (!onlyBinding) {
850                        return;
851                    }
852                    return onlyBinding.name;
853                }
854                else if (isNamespaceImport(node.importClause.namedBindings)) {
855                    return node.importClause.namedBindings.name;
856                }
857            }
858        }
859        if (!forRename) {
860            // /**/import "[|module|]";
861            // /**/import ... from "[|module|]";
862            // import /**/type ... from "[|module|]";
863            return node.moduleSpecifier;
864        }
865    }
866
867    function getAdjustedLocationForExportDeclaration(node: ExportDeclaration, forRename: boolean) {
868        if (node.exportClause) {
869            // /**/export { [|name|] } ...
870            // /**/export { propertyName as [|name|] } ...
871            // /**/export * as [|name|] ...
872            // export /**/type { [|name|] } from ...
873            // export /**/type { propertyName as [|name|] } from ...
874            // export /**/type * as [|name|] ...
875            if (isNamedExports(node.exportClause)) {
876                // do nothing if there is more than one binding
877                const onlyBinding = singleOrUndefined(node.exportClause.elements);
878                if (!onlyBinding) {
879                    return;
880                }
881                return node.exportClause.elements[0].name;
882            }
883            else if (isNamespaceExport(node.exportClause)) {
884                return node.exportClause.name;
885            }
886        }
887        if (!forRename) {
888            // /**/export * from "[|module|]";
889            // export /**/type * from "[|module|]";
890            return node.moduleSpecifier;
891        }
892    }
893
894    function getAdjustedLocationForHeritageClause(node: HeritageClause) {
895        // /**/extends [|name|]
896        // /**/implements [|name|]
897        if (node.types.length === 1) {
898            return node.types[0].expression;
899        }
900
901        // /**/extends name1, name2 ...
902        // /**/implements name1, name2 ...
903    }
904
905    function getAdjustedLocation(node: Node, forRename: boolean): Node {
906        const { parent } = node;
907        // /**/<modifier> [|name|] ...
908        // /**/<modifier> <class|interface|type|enum|module|namespace|function|get|set> [|name|] ...
909        // /**/<class|interface|type|enum|module|namespace|function|get|set> [|name|] ...
910        // /**/import [|name|] = ...
911        //
912        // NOTE: If the node is a modifier, we don't adjust its location if it is the `default` modifier as that is handled
913        // specially by `getSymbolAtLocation`.
914        if (isModifier(node) && (forRename || node.kind !== SyntaxKind.DefaultKeyword) ? canHaveModifiers(parent) && contains(parent.modifiers, node) :
915            node.kind === SyntaxKind.ClassKeyword ? isClassDeclaration(parent) || isClassExpression(node) :
916                node.kind === SyntaxKind.FunctionKeyword ? isFunctionDeclaration(parent) || isFunctionExpression(node) :
917                    node.kind === SyntaxKind.InterfaceKeyword ? isInterfaceDeclaration(parent) :
918                        node.kind === SyntaxKind.EnumKeyword ? isEnumDeclaration(parent) :
919                            node.kind === SyntaxKind.TypeKeyword ? isTypeAliasDeclaration(parent) :
920                                node.kind === SyntaxKind.NamespaceKeyword || node.kind === SyntaxKind.ModuleKeyword ? isModuleDeclaration(parent) :
921                                    node.kind === SyntaxKind.ImportKeyword ? isImportEqualsDeclaration(parent) :
922                                        node.kind === SyntaxKind.GetKeyword ? isGetAccessorDeclaration(parent) :
923                                            node.kind === SyntaxKind.SetKeyword && isSetAccessorDeclaration(parent)) {
924            const location = getAdjustedLocationForDeclaration(parent, forRename);
925            if (location) {
926                return location;
927            }
928        }
929        // /**/<var|let|const> [|name|] ...
930        if ((node.kind === SyntaxKind.VarKeyword || node.kind === SyntaxKind.ConstKeyword || node.kind === SyntaxKind.LetKeyword) &&
931            isVariableDeclarationList(parent) && parent.declarations.length === 1) {
932            const decl = parent.declarations[0];
933            if (isIdentifier(decl.name)) {
934                return decl.name;
935            }
936        }
937        if (node.kind === SyntaxKind.TypeKeyword) {
938            // import /**/type [|name|] from ...;
939            // import /**/type { [|name|] } from ...;
940            // import /**/type { propertyName as [|name|] } from ...;
941            // import /**/type ... from "[|module|]";
942            if (isImportClause(parent) && parent.isTypeOnly) {
943                const location = getAdjustedLocationForImportDeclaration(parent.parent, forRename);
944                if (location) {
945                    return location;
946                }
947            }
948            // export /**/type { [|name|] } from ...;
949            // export /**/type { propertyName as [|name|] } from ...;
950            // export /**/type * from "[|module|]";
951            // export /**/type * as ... from "[|module|]";
952            if (isExportDeclaration(parent) && parent.isTypeOnly) {
953                const location = getAdjustedLocationForExportDeclaration(parent, forRename);
954                if (location) {
955                    return location;
956                }
957            }
958        }
959        // import { propertyName /**/as [|name|] } ...
960        // import * /**/as [|name|] ...
961        // export { propertyName /**/as [|name|] } ...
962        // export * /**/as [|name|] ...
963        if (node.kind === SyntaxKind.AsKeyword) {
964            if (isImportSpecifier(parent) && parent.propertyName ||
965                isExportSpecifier(parent) && parent.propertyName ||
966                isNamespaceImport(parent) ||
967                isNamespaceExport(parent)) {
968                return parent.name;
969            }
970            if (isExportDeclaration(parent) && parent.exportClause && isNamespaceExport(parent.exportClause)) {
971                return parent.exportClause.name;
972            }
973        }
974        // /**/import [|name|] from ...;
975        // /**/import { [|name|] } from ...;
976        // /**/import { propertyName as [|name|] } from ...;
977        // /**/import ... from "[|module|]";
978        // /**/import "[|module|]";
979        if (node.kind === SyntaxKind.ImportKeyword && isImportDeclaration(parent)) {
980            const location = getAdjustedLocationForImportDeclaration(parent, forRename);
981            if (location) {
982                return location;
983            }
984        }
985        if (node.kind === SyntaxKind.ExportKeyword) {
986            // /**/export { [|name|] } ...;
987            // /**/export { propertyName as [|name|] } ...;
988            // /**/export * from "[|module|]";
989            // /**/export * as ... from "[|module|]";
990            if (isExportDeclaration(parent)) {
991                const location = getAdjustedLocationForExportDeclaration(parent, forRename);
992                if (location) {
993                    return location;
994                }
995            }
996            // NOTE: We don't adjust the location of the `default` keyword as that is handled specially by `getSymbolAtLocation`.
997            // /**/export default [|name|];
998            // /**/export = [|name|];
999            if (isExportAssignment(parent)) {
1000                return skipOuterExpressions(parent.expression);
1001            }
1002        }
1003        // import name = /**/require("[|module|]");
1004        if (node.kind === SyntaxKind.RequireKeyword && isExternalModuleReference(parent)) {
1005            return parent.expression;
1006        }
1007        // import ... /**/from "[|module|]";
1008        // export ... /**/from "[|module|]";
1009        if (node.kind === SyntaxKind.FromKeyword && (isImportDeclaration(parent) || isExportDeclaration(parent)) && parent.moduleSpecifier) {
1010            return parent.moduleSpecifier;
1011        }
1012        // class ... /**/extends [|name|] ...
1013        // class ... /**/implements [|name|] ...
1014        // class ... /**/implements name1, name2 ...
1015        // interface ... /**/extends [|name|] ...
1016        // interface ... /**/extends name1, name2 ...
1017        if ((node.kind === SyntaxKind.ExtendsKeyword || node.kind === SyntaxKind.ImplementsKeyword) && isHeritageClause(parent) && parent.token === node.kind) {
1018            const location = getAdjustedLocationForHeritageClause(parent);
1019            if (location) {
1020                return location;
1021            }
1022        }
1023        if (node.kind === SyntaxKind.ExtendsKeyword) {
1024            // ... <T /**/extends [|U|]> ...
1025            if (isTypeParameterDeclaration(parent) && parent.constraint && isTypeReferenceNode(parent.constraint)) {
1026                return parent.constraint.typeName;
1027            }
1028            // ... T /**/extends [|U|] ? ...
1029            if (isConditionalTypeNode(parent) && isTypeReferenceNode(parent.extendsType)) {
1030                return parent.extendsType.typeName;
1031            }
1032        }
1033        // ... T extends /**/infer [|U|] ? ...
1034        if (node.kind === SyntaxKind.InferKeyword && isInferTypeNode(parent)) {
1035            return parent.typeParameter.name;
1036        }
1037        // { [ [|K|] /**/in keyof T]: ... }
1038        if (node.kind === SyntaxKind.InKeyword && isTypeParameterDeclaration(parent) && isMappedTypeNode(parent.parent)) {
1039            return parent.name;
1040        }
1041        // /**/keyof [|T|]
1042        if (node.kind === SyntaxKind.KeyOfKeyword && isTypeOperatorNode(parent) && parent.operator === SyntaxKind.KeyOfKeyword &&
1043            isTypeReferenceNode(parent.type)) {
1044            return parent.type.typeName;
1045        }
1046        // /**/readonly [|name|][]
1047        if (node.kind === SyntaxKind.ReadonlyKeyword && isTypeOperatorNode(parent) && parent.operator === SyntaxKind.ReadonlyKeyword &&
1048            isArrayTypeNode(parent.type) && isTypeReferenceNode(parent.type.elementType)) {
1049            return parent.type.elementType.typeName;
1050        }
1051        if (!forRename) {
1052            // /**/new [|name|]
1053            // /**/void [|name|]
1054            // /**/void obj.[|name|]
1055            // /**/typeof [|name|]
1056            // /**/typeof obj.[|name|]
1057            // /**/await [|name|]
1058            // /**/await obj.[|name|]
1059            // /**/yield [|name|]
1060            // /**/yield obj.[|name|]
1061            // /**/delete obj.[|name|]
1062            if (node.kind === SyntaxKind.NewKeyword && isNewExpression(parent) ||
1063                node.kind === SyntaxKind.VoidKeyword && isVoidExpression(parent) ||
1064                node.kind === SyntaxKind.TypeOfKeyword && isTypeOfExpression(parent) ||
1065                node.kind === SyntaxKind.AwaitKeyword && isAwaitExpression(parent) ||
1066                node.kind === SyntaxKind.YieldKeyword && isYieldExpression(parent) ||
1067                node.kind === SyntaxKind.DeleteKeyword && isDeleteExpression(parent)) {
1068                if (parent.expression) {
1069                    return skipOuterExpressions(parent.expression);
1070                }
1071            }
1072            // left /**/in [|name|]
1073            // left /**/instanceof [|name|]
1074            if ((node.kind === SyntaxKind.InKeyword || node.kind === SyntaxKind.InstanceOfKeyword) && isBinaryExpression(parent) && parent.operatorToken === node) {
1075                return skipOuterExpressions(parent.right);
1076            }
1077            // left /**/as [|name|]
1078            if (node.kind === SyntaxKind.AsKeyword && isAsExpression(parent) && isTypeReferenceNode(parent.type)) {
1079                return parent.type.typeName;
1080            }
1081            // for (... /**/in [|name|])
1082            // for (... /**/of [|name|])
1083            if (node.kind === SyntaxKind.InKeyword && isForInStatement(parent) ||
1084                node.kind === SyntaxKind.OfKeyword && isForOfStatement(parent)) {
1085                return skipOuterExpressions(parent.expression);
1086            }
1087        }
1088        return node;
1089    }
1090
1091    /**
1092     * Adjusts the location used for "find references" and "go to definition" when the cursor was not
1093     * on a property name.
1094     */
1095    export function getAdjustedReferenceLocation(node: Node): Node {
1096        return getAdjustedLocation(node, /*forRename*/ false);
1097    }
1098
1099    /**
1100     * Adjusts the location used for "rename" when the cursor was not on a property name.
1101     */
1102    export function getAdjustedRenameLocation(node: Node): Node {
1103        return getAdjustedLocation(node, /*forRename*/ true);
1104    }
1105
1106    /**
1107     * Gets the token whose text has range [start, end) and
1108     * position >= start and (position < end or (position === end && token is literal or keyword or identifier))
1109     */
1110    export function getTouchingPropertyName(sourceFile: SourceFile, position: number): Node {
1111        return getTouchingToken(sourceFile, position, n => isPropertyNameLiteral(n) || isKeyword(n.kind) || isPrivateIdentifier(n));
1112    }
1113
1114    /**
1115     * Returns the token if position is in [start, end).
1116     * If position === end, returns the preceding token if includeItemAtEndPosition(previousToken) === true
1117     */
1118    export function getTouchingToken(sourceFile: SourceFile, position: number, includePrecedingTokenAtEndPosition?: (n: Node) => boolean): Node {
1119        return getTokenAtPositionWorker(sourceFile, position, /*allowPositionInLeadingTrivia*/ false, includePrecedingTokenAtEndPosition, /*includeEndPosition*/ false);
1120    }
1121
1122    /** Returns a token if position is in [start-of-leading-trivia, end) */
1123    export function getTokenAtPosition(sourceFile: SourceFile, position: number): Node {
1124        return getTokenAtPositionWorker(sourceFile, position, /*allowPositionInLeadingTrivia*/ true, /*includePrecedingTokenAtEndPosition*/ undefined, /*includeEndPosition*/ false);
1125    }
1126
1127    /** Get the token whose text contains the position */
1128    function getTokenAtPositionWorker(sourceFile: SourceFile, position: number, allowPositionInLeadingTrivia: boolean, includePrecedingTokenAtEndPosition: ((n: Node) => boolean) | undefined, includeEndPosition: boolean): Node {
1129        let current: Node = sourceFile;
1130        let foundToken: Node | undefined;
1131        outer: while (true) {
1132            // find the child that contains 'position'
1133
1134            const children = current.getChildren(sourceFile);
1135            const i = binarySearchKey(children, position, (_, i) => i, (middle, _) => {
1136                // This last callback is more of a selector than a comparator -
1137                // `EqualTo` causes the `middle` result to be returned
1138                // `GreaterThan` causes recursion on the left of the middle
1139                // `LessThan` causes recursion on the right of the middle
1140
1141                // Let's say you have 3 nodes, spanning positons
1142                // pos: 1, end: 3
1143                // pos: 3, end: 3
1144                // pos: 3, end: 5
1145                // and you're looking for the token at positon 3 - all 3 of these nodes are overlapping with position 3.
1146                // In fact, there's a _good argument_ that node 2 shouldn't even be allowed to exist - depending on if
1147                // the start or end of the ranges are considered inclusive, it's either wholly subsumed by the first or the last node.
1148                // Unfortunately, such nodes do exist. :( - See fourslash/completionsImport_tsx.tsx - empty jsx attributes create
1149                // a zero-length node.
1150                // What also you may not expect is that which node we return depends on the includePrecedingTokenAtEndPosition flag.
1151                // Specifically, if includePrecedingTokenAtEndPosition is set, we return the 1-3 node, while if it's unset, we
1152                // return the 3-5 node. (The zero length node is never correct.) This is because the includePrecedingTokenAtEndPosition
1153                // flag causes us to return the first node whose end position matches the position and which produces and acceptable token
1154                // kind. Meanwhile, if includePrecedingTokenAtEndPosition is unset, we look for the first node whose start is <= the
1155                // position and whose end is greater than the position.
1156
1157
1158                // There are more sophisticated end tests later, but this one is very fast
1159                // and allows us to skip a bunch of work
1160                const end = children[middle].getEnd();
1161                if (end < position) {
1162                    return Comparison.LessThan;
1163                }
1164
1165                const start = allowPositionInLeadingTrivia ? children[middle].getFullStart() : children[middle].getStart(sourceFile, /*includeJsDoc*/ true);
1166                if (start > position) {
1167                    return Comparison.GreaterThan;
1168                }
1169
1170                // first element whose start position is before the input and whose end position is after or equal to the input
1171                if (nodeContainsPosition(children[middle], start, end)) {
1172                    if (children[middle - 1]) {
1173                        // we want the _first_ element that contains the position, so left-recur if the prior node also contains the position
1174                        if (nodeContainsPosition(children[middle - 1])) {
1175                            return Comparison.GreaterThan;
1176                        }
1177                    }
1178                    return Comparison.EqualTo;
1179                }
1180
1181                // this complex condition makes us left-recur around a zero-length node when includePrecedingTokenAtEndPosition is set, rather than right-recur on it
1182                if (includePrecedingTokenAtEndPosition && start === position && children[middle - 1] && children[middle - 1].getEnd() === position && nodeContainsPosition(children[middle - 1])) {
1183                    return Comparison.GreaterThan;
1184                }
1185                return Comparison.LessThan;
1186            });
1187
1188            if (foundToken) {
1189                return foundToken;
1190            }
1191            if (i >= 0 && children[i]) {
1192                current = children[i];
1193                continue outer;
1194            }
1195
1196            return current;
1197        }
1198
1199        function nodeContainsPosition(node: Node, start?: number, end?: number) {
1200            end ??= node.getEnd();
1201            if (end < position) {
1202                return false;
1203            }
1204            start ??= allowPositionInLeadingTrivia ? node.getFullStart() : node.getStart(sourceFile, /*includeJsDoc*/ true);
1205            if (start > position) {
1206                // If this child begins after position, then all subsequent children will as well.
1207                return false;
1208            }
1209            if (position < end || (position === end && (node.kind === SyntaxKind.EndOfFileToken || includeEndPosition))) {
1210                return true;
1211            }
1212            else if (includePrecedingTokenAtEndPosition && end === position) {
1213                const previousToken = findPrecedingToken(position, sourceFile, node);
1214                if (previousToken && includePrecedingTokenAtEndPosition(previousToken)) {
1215                    foundToken = previousToken;
1216                    return true;
1217                }
1218            }
1219            return false;
1220        }
1221    }
1222
1223    /**
1224     * Returns the first token where position is in [start, end),
1225     * excluding `JsxText` tokens containing only whitespace.
1226     */
1227    export function findFirstNonJsxWhitespaceToken(sourceFile: SourceFile, position: number): Node | undefined {
1228        let tokenAtPosition = getTokenAtPosition(sourceFile, position);
1229        while (isWhiteSpaceOnlyJsxText(tokenAtPosition)) {
1230            const nextToken = findNextToken(tokenAtPosition, tokenAtPosition.parent, sourceFile);
1231            if (!nextToken) return;
1232            tokenAtPosition = nextToken;
1233        }
1234        return tokenAtPosition;
1235    }
1236
1237    /**
1238     * The token on the left of the position is the token that strictly includes the position
1239     * or sits to the left of the cursor if it is on a boundary. For example
1240     *
1241     *   fo|o               -> will return foo
1242     *   foo <comment> |bar -> will return foo
1243     *
1244     */
1245    export function findTokenOnLeftOfPosition(file: SourceFile, position: number): Node | undefined {
1246        // Ideally, getTokenAtPosition should return a token. However, it is currently
1247        // broken, so we do a check to make sure the result was indeed a token.
1248        const tokenAtPosition = getTokenAtPosition(file, position);
1249        if (isToken(tokenAtPosition) && position > tokenAtPosition.getStart(file) && position < tokenAtPosition.getEnd()) {
1250            return tokenAtPosition;
1251        }
1252
1253        return findPrecedingToken(position, file);
1254    }
1255
1256    export function findNextToken(previousToken: Node, parent: Node, sourceFile: SourceFileLike): Node | undefined {
1257        return find(parent);
1258
1259        function find(n: Node): Node | undefined {
1260            if (isToken(n) && n.pos === previousToken.end) {
1261                // this is token that starts at the end of previous token - return it
1262                return n;
1263            }
1264            return firstDefined(n.getChildren(sourceFile), child => {
1265                const shouldDiveInChildNode =
1266                    // previous token is enclosed somewhere in the child
1267                    (child.pos <= previousToken.pos && child.end > previousToken.end) ||
1268                    // previous token ends exactly at the beginning of child
1269                    (child.pos === previousToken.end);
1270                return shouldDiveInChildNode && nodeHasTokens(child, sourceFile) ? find(child) : undefined;
1271            });
1272        }
1273    }
1274
1275    /**
1276     * Finds the rightmost token satisfying `token.end <= position`,
1277     * excluding `JsxText` tokens containing only whitespace.
1278     */
1279    export function findPrecedingToken(position: number, sourceFile: SourceFileLike, startNode: Node, excludeJsdoc?: boolean): Node | undefined;
1280    export function findPrecedingToken(position: number, sourceFile: SourceFile, startNode?: Node, excludeJsdoc?: boolean): Node | undefined;
1281    export function findPrecedingToken(position: number, sourceFile: SourceFileLike, startNode?: Node, excludeJsdoc?: boolean): Node | undefined {
1282        const result = find((startNode || sourceFile) as Node);
1283        Debug.assert(!(result && isWhiteSpaceOnlyJsxText(result)));
1284        return result;
1285
1286        function find(n: Node): Node | undefined {
1287            if (isNonWhitespaceToken(n) && n.kind !== SyntaxKind.EndOfFileToken) {
1288                return n;
1289            }
1290
1291            const children = n.getChildren(sourceFile);
1292            const i = binarySearchKey(children, position, (_, i) => i, (middle, _) => {
1293                // This last callback is more of a selector than a comparator -
1294                // `EqualTo` causes the `middle` result to be returned
1295                // `GreaterThan` causes recursion on the left of the middle
1296                // `LessThan` causes recursion on the right of the middle
1297                if (position < children[middle].end) {
1298                    // first element whose end position is greater than the input position
1299                    if (!children[middle - 1] || position >= children[middle - 1].end) {
1300                        return Comparison.EqualTo;
1301                    }
1302                    return Comparison.GreaterThan;
1303                }
1304                return Comparison.LessThan;
1305            });
1306            if (i >= 0 && children[i]) {
1307                const child = children[i];
1308                // Note that the span of a node's tokens is [node.getStart(...), node.end).
1309                // Given that `position < child.end` and child has constituent tokens, we distinguish these cases:
1310                // 1) `position` precedes `child`'s tokens or `child` has no tokens (ie: in a comment or whitespace preceding `child`):
1311                // we need to find the last token in a previous child.
1312                // 2) `position` is within the same span: we recurse on `child`.
1313                if (position < child.end) {
1314                    const start = child.getStart(sourceFile, /*includeJsDoc*/ !excludeJsdoc);
1315                    const lookInPreviousChild =
1316                        (start >= position) || // cursor in the leading trivia
1317                        !nodeHasTokens(child, sourceFile) ||
1318                        isWhiteSpaceOnlyJsxText(child);
1319
1320                    if (lookInPreviousChild) {
1321                        // actual start of the node is past the position - previous token should be at the end of previous child
1322                        const candidate = findRightmostChildNodeWithTokens(children, /*exclusiveStartPosition*/ i, sourceFile, n.kind);
1323                        return candidate && findRightmostToken(candidate, sourceFile);
1324                    }
1325                    else {
1326                        // candidate should be in this node
1327                        return find(child);
1328                    }
1329                }
1330            }
1331
1332            Debug.assert(startNode !== undefined || n.kind === SyntaxKind.SourceFile || n.kind === SyntaxKind.EndOfFileToken || isJSDocCommentContainingNode(n));
1333
1334            // Here we know that none of child token nodes embrace the position,
1335            // the only known case is when position is at the end of the file.
1336            // Try to find the rightmost token in the file without filtering.
1337            // Namely we are skipping the check: 'position < node.end'
1338            const candidate = findRightmostChildNodeWithTokens(children, /*exclusiveStartPosition*/ children.length, sourceFile, n.kind);
1339            return candidate && findRightmostToken(candidate, sourceFile);
1340        }
1341    }
1342
1343    function isNonWhitespaceToken(n: Node): boolean {
1344        return isToken(n) && !isWhiteSpaceOnlyJsxText(n);
1345    }
1346
1347    function findRightmostToken(n: Node, sourceFile: SourceFileLike): Node | undefined {
1348        if (isNonWhitespaceToken(n)) {
1349            return n;
1350        }
1351
1352        const children = n.getChildren(sourceFile);
1353        if (children.length === 0) {
1354            return n;
1355        }
1356
1357        const candidate = findRightmostChildNodeWithTokens(children, /*exclusiveStartPosition*/ children.length, sourceFile, n.kind);
1358        return candidate && findRightmostToken(candidate, sourceFile);
1359    }
1360
1361    /**
1362     * Finds the rightmost child to the left of `children[exclusiveStartPosition]` which is a non-all-whitespace token or has constituent tokens.
1363     */
1364    function findRightmostChildNodeWithTokens(children: Node[], exclusiveStartPosition: number, sourceFile: SourceFileLike, parentKind: SyntaxKind): Node | undefined {
1365        for (let i = exclusiveStartPosition - 1; i >= 0; i--) {
1366            const child = children[i];
1367
1368            if (isWhiteSpaceOnlyJsxText(child)) {
1369                if (i === 0 && (parentKind === SyntaxKind.JsxText || parentKind === SyntaxKind.JsxSelfClosingElement)) {
1370                    Debug.fail("`JsxText` tokens should not be the first child of `JsxElement | JsxSelfClosingElement`");
1371                }
1372            }
1373            else if (nodeHasTokens(children[i], sourceFile)) {
1374                return children[i];
1375            }
1376        }
1377    }
1378
1379    export function isInString(sourceFile: SourceFile, position: number, previousToken = findPrecedingToken(position, sourceFile)): boolean {
1380        if (previousToken && isStringTextContainingNode(previousToken)) {
1381            const start = previousToken.getStart(sourceFile);
1382            const end = previousToken.getEnd();
1383
1384            // To be "in" one of these literals, the position has to be:
1385            //   1. entirely within the token text.
1386            //   2. at the end position of an unterminated token.
1387            //   3. at the end of a regular expression (due to trailing flags like '/foo/g').
1388            if (start < position && position < end) {
1389                return true;
1390            }
1391
1392            if (position === end) {
1393                return !!(previousToken as LiteralExpression).isUnterminated;
1394            }
1395        }
1396
1397        return false;
1398    }
1399
1400    /**
1401     * returns true if the position is in between the open and close elements of an JSX expression.
1402     */
1403    export function isInsideJsxElementOrAttribute(sourceFile: SourceFile, position: number) {
1404        const token = getTokenAtPosition(sourceFile, position);
1405
1406        if (!token) {
1407            return false;
1408        }
1409
1410        if (token.kind === SyntaxKind.JsxText) {
1411            return true;
1412        }
1413
1414        // <div>Hello |</div>
1415        if (token.kind === SyntaxKind.LessThanToken && token.parent.kind === SyntaxKind.JsxText) {
1416            return true;
1417        }
1418
1419        // <div> { | </div> or <div a={| </div>
1420        if (token.kind === SyntaxKind.LessThanToken && token.parent.kind === SyntaxKind.JsxExpression) {
1421            return true;
1422        }
1423
1424        // <div> {
1425        // |
1426        // } < /div>
1427        if (token && token.kind === SyntaxKind.CloseBraceToken && token.parent.kind === SyntaxKind.JsxExpression) {
1428            return true;
1429        }
1430
1431        // <div>|</div>
1432        if (token.kind === SyntaxKind.LessThanToken && token.parent.kind === SyntaxKind.JsxClosingElement) {
1433            return true;
1434        }
1435
1436        return false;
1437    }
1438
1439    function isWhiteSpaceOnlyJsxText(node: Node): boolean {
1440        return isJsxText(node) && node.containsOnlyTriviaWhiteSpaces;
1441    }
1442
1443    export function isInTemplateString(sourceFile: SourceFile, position: number) {
1444        const token = getTokenAtPosition(sourceFile, position);
1445        return isTemplateLiteralKind(token.kind) && position > token.getStart(sourceFile);
1446    }
1447
1448    export function isInJSXText(sourceFile: SourceFile, position: number) {
1449        const token = getTokenAtPosition(sourceFile, position);
1450        if (isJsxText(token)) {
1451            return true;
1452        }
1453        if (token.kind === SyntaxKind.OpenBraceToken && isJsxExpression(token.parent) && isJsxElement(token.parent.parent)) {
1454            return true;
1455        }
1456        if (token.kind === SyntaxKind.LessThanToken && isJsxOpeningLikeElement(token.parent) && isJsxElement(token.parent.parent)) {
1457            return true;
1458        }
1459        return false;
1460    }
1461
1462    export function isInsideJsxElement(sourceFile: SourceFile, position: number): boolean {
1463        function isInsideJsxElementTraversal(node: Node): boolean {
1464            while (node) {
1465                if (node.kind >= SyntaxKind.JsxSelfClosingElement && node.kind <= SyntaxKind.JsxExpression
1466                    || node.kind === SyntaxKind.JsxText
1467                    || node.kind === SyntaxKind.LessThanToken
1468                    || node.kind === SyntaxKind.GreaterThanToken
1469                    || node.kind === SyntaxKind.Identifier
1470                    || node.kind === SyntaxKind.CloseBraceToken
1471                    || node.kind === SyntaxKind.OpenBraceToken
1472                    || node.kind === SyntaxKind.SlashToken) {
1473                    node = node.parent;
1474                }
1475                else if (node.kind === SyntaxKind.JsxElement) {
1476                    if (position > node.getStart(sourceFile)) return true;
1477
1478                    node = node.parent;
1479                }
1480                else {
1481                    return false;
1482                }
1483            }
1484
1485            return false;
1486        }
1487
1488        return isInsideJsxElementTraversal(getTokenAtPosition(sourceFile, position));
1489    }
1490
1491    export function findPrecedingMatchingToken(token: Node, matchingTokenKind: SyntaxKind.OpenBraceToken | SyntaxKind.OpenParenToken | SyntaxKind.OpenBracketToken, sourceFile: SourceFile) {
1492        const closeTokenText = tokenToString(token.kind)!;
1493        const matchingTokenText = tokenToString(matchingTokenKind)!;
1494        const tokenFullStart = token.getFullStart();
1495        // Text-scan based fast path - can be bamboozled by comments and other trivia, but often provides
1496        // a good, fast approximation without too much extra work in the cases where it fails.
1497        const bestGuessIndex = sourceFile.text.lastIndexOf(matchingTokenText, tokenFullStart);
1498        if (bestGuessIndex === -1) {
1499            return undefined; // if the token text doesn't appear in the file, there can't be a match - super fast bail
1500        }
1501        // we can only use the textual result directly if we didn't have to count any close tokens within the range
1502        if (sourceFile.text.lastIndexOf(closeTokenText, tokenFullStart - 1) < bestGuessIndex) {
1503            const nodeAtGuess = findPrecedingToken(bestGuessIndex + 1, sourceFile);
1504            if (nodeAtGuess && nodeAtGuess.kind === matchingTokenKind) {
1505                return nodeAtGuess;
1506            }
1507        }
1508        const tokenKind = token.kind;
1509        let remainingMatchingTokens = 0;
1510        while (true) {
1511            const preceding = findPrecedingToken(token.getFullStart(), sourceFile);
1512            if (!preceding) {
1513                return undefined;
1514            }
1515            token = preceding;
1516
1517            if (token.kind === matchingTokenKind) {
1518                if (remainingMatchingTokens === 0) {
1519                    return token;
1520                }
1521
1522                remainingMatchingTokens--;
1523            }
1524            else if (token.kind === tokenKind) {
1525                remainingMatchingTokens++;
1526            }
1527        }
1528    }
1529
1530    export function removeOptionality(type: Type, isOptionalExpression: boolean, isOptionalChain: boolean) {
1531        return isOptionalExpression ? type.getNonNullableType() :
1532            isOptionalChain ? type.getNonOptionalType() :
1533                type;
1534    }
1535
1536    export function isPossiblyTypeArgumentPosition(token: Node, sourceFile: SourceFile, checker: TypeChecker): boolean {
1537        const info = getPossibleTypeArgumentsInfo(token, sourceFile);
1538        return info !== undefined && (isPartOfTypeNode(info.called) ||
1539            getPossibleGenericSignatures(info.called, info.nTypeArguments, checker).length !== 0 ||
1540            isPossiblyTypeArgumentPosition(info.called, sourceFile, checker));
1541    }
1542
1543    export function getPossibleGenericSignatures(called: Expression, typeArgumentCount: number, checker: TypeChecker): readonly Signature[] {
1544        let type = checker.getTypeAtLocation(called);
1545        if (isOptionalChain(called.parent)) {
1546            type = removeOptionality(type, isOptionalChainRoot(called.parent), /*isOptionalChain*/ true);
1547        }
1548
1549        const signatures = isNewExpression(called.parent) ? type.getConstructSignatures() : type.getCallSignatures();
1550        return signatures.filter(candidate => !!candidate.typeParameters && candidate.typeParameters.length >= typeArgumentCount);
1551    }
1552
1553    export interface PossibleTypeArgumentInfo {
1554        readonly called: Identifier;
1555        readonly nTypeArguments: number;
1556    }
1557
1558    export interface PossibleProgramFileInfo {
1559        ProgramFiles?: string[];
1560    }
1561
1562    // Get info for an expression like `f <` that may be the start of type arguments.
1563    export function getPossibleTypeArgumentsInfo(tokenIn: Node | undefined, sourceFile: SourceFile): PossibleTypeArgumentInfo | undefined {
1564        // This is a rare case, but one that saves on a _lot_ of work if true - if the source file has _no_ `<` character,
1565        // then there obviously can't be any type arguments - no expensive brace-matching backwards scanning required
1566
1567        if (sourceFile.text.lastIndexOf("<", tokenIn ? tokenIn.pos : sourceFile.text.length) === -1) {
1568            return undefined;
1569        }
1570
1571        let token: Node | undefined = tokenIn;
1572        // This function determines if the node could be type argument position
1573        // Since during editing, when type argument list is not complete,
1574        // the tree could be of any shape depending on the tokens parsed before current node,
1575        // scanning of the previous identifier followed by "<" before current node would give us better result
1576        // Note that we also balance out the already provided type arguments, arrays, object literals while doing so
1577        let remainingLessThanTokens = 0;
1578        let nTypeArguments = 0;
1579        while (token) {
1580            switch (token.kind) {
1581                case SyntaxKind.LessThanToken:
1582                    // Found the beginning of the generic argument expression
1583                    token = findPrecedingToken(token.getFullStart(), sourceFile);
1584                    if (token && token.kind === SyntaxKind.QuestionDotToken) {
1585                        token = findPrecedingToken(token.getFullStart(), sourceFile);
1586                    }
1587                    if (!token || !isIdentifier(token)) return undefined;
1588                    if (!remainingLessThanTokens) {
1589                        return isDeclarationName(token) ? undefined : { called: token, nTypeArguments };
1590                    }
1591                    remainingLessThanTokens--;
1592                    break;
1593
1594                case SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
1595                    remainingLessThanTokens = + 3;
1596                    break;
1597
1598                case SyntaxKind.GreaterThanGreaterThanToken:
1599                    remainingLessThanTokens = + 2;
1600                    break;
1601
1602                case SyntaxKind.GreaterThanToken:
1603                    remainingLessThanTokens++;
1604                    break;
1605
1606                case SyntaxKind.CloseBraceToken:
1607                    // This can be object type, skip until we find the matching open brace token
1608                    // Skip until the matching open brace token
1609                    token = findPrecedingMatchingToken(token, SyntaxKind.OpenBraceToken, sourceFile);
1610                    if (!token) return undefined;
1611                    break;
1612
1613                case SyntaxKind.CloseParenToken:
1614                    // This can be object type, skip until we find the matching open brace token
1615                    // Skip until the matching open brace token
1616                    token = findPrecedingMatchingToken(token, SyntaxKind.OpenParenToken, sourceFile);
1617                    if (!token) return undefined;
1618                    break;
1619
1620                case SyntaxKind.CloseBracketToken:
1621                    // This can be object type, skip until we find the matching open brace token
1622                    // Skip until the matching open brace token
1623                    token = findPrecedingMatchingToken(token, SyntaxKind.OpenBracketToken, sourceFile);
1624                    if (!token) return undefined;
1625                    break;
1626
1627                // Valid tokens in a type name. Skip.
1628                case SyntaxKind.CommaToken:
1629                    nTypeArguments++;
1630                    break;
1631
1632                case SyntaxKind.EqualsGreaterThanToken:
1633                // falls through
1634
1635                case SyntaxKind.Identifier:
1636                case SyntaxKind.StringLiteral:
1637                case SyntaxKind.NumericLiteral:
1638                case SyntaxKind.BigIntLiteral:
1639                case SyntaxKind.TrueKeyword:
1640                case SyntaxKind.FalseKeyword:
1641                // falls through
1642
1643                case SyntaxKind.TypeOfKeyword:
1644                case SyntaxKind.ExtendsKeyword:
1645                case SyntaxKind.KeyOfKeyword:
1646                case SyntaxKind.DotToken:
1647                case SyntaxKind.BarToken:
1648                case SyntaxKind.QuestionToken:
1649                case SyntaxKind.ColonToken:
1650                    break;
1651
1652                default:
1653                    if (isTypeNode(token)) {
1654                        break;
1655                    }
1656
1657                    // Invalid token in type
1658                    return undefined;
1659            }
1660
1661            token = findPrecedingToken(token.getFullStart(), sourceFile);
1662        }
1663
1664        return undefined;
1665    }
1666
1667    /**
1668     * Returns true if the cursor at position in sourceFile is within a comment.
1669     *
1670     * @param tokenAtPosition Must equal `getTokenAtPosition(sourceFile, position)`
1671     * @param predicate Additional predicate to test on the comment range.
1672     */
1673    export function isInComment(sourceFile: SourceFile, position: number, tokenAtPosition?: Node): CommentRange | undefined {
1674        return formatting.getRangeOfEnclosingComment(sourceFile, position, /*precedingToken*/ undefined, tokenAtPosition);
1675    }
1676
1677    export function hasDocComment(sourceFile: SourceFile, position: number): boolean {
1678        const token = getTokenAtPosition(sourceFile, position);
1679        return !!findAncestor(token, isJSDoc);
1680    }
1681
1682    function nodeHasTokens(n: Node, sourceFile: SourceFileLike): boolean {
1683        // If we have a token or node that has a non-zero width, it must have tokens.
1684        // Note: getWidth() does not take trivia into account.
1685        return n.kind === SyntaxKind.EndOfFileToken ? !!(n as EndOfFileToken).jsDoc : n.getWidth(sourceFile) !== 0;
1686    }
1687
1688    export function getNodeModifiers(node: Node, excludeFlags = ModifierFlags.None): string {
1689        const result: string[] = [];
1690        const flags = isDeclaration(node)
1691            ? getCombinedNodeFlagsAlwaysIncludeJSDoc(node) & ~excludeFlags
1692            : ModifierFlags.None;
1693
1694        if (flags & ModifierFlags.Private) result.push(ScriptElementKindModifier.privateMemberModifier);
1695        if (flags & ModifierFlags.Protected) result.push(ScriptElementKindModifier.protectedMemberModifier);
1696        if (flags & ModifierFlags.Public) result.push(ScriptElementKindModifier.publicMemberModifier);
1697        if (flags & ModifierFlags.Static || isClassStaticBlockDeclaration(node)) result.push(ScriptElementKindModifier.staticModifier);
1698        if (flags & ModifierFlags.Abstract) result.push(ScriptElementKindModifier.abstractModifier);
1699        if (flags & ModifierFlags.Export) result.push(ScriptElementKindModifier.exportedModifier);
1700        if (flags & ModifierFlags.Deprecated) result.push(ScriptElementKindModifier.deprecatedModifier);
1701        if (node.flags & NodeFlags.Ambient) result.push(ScriptElementKindModifier.ambientModifier);
1702        if (node.kind === SyntaxKind.ExportAssignment) result.push(ScriptElementKindModifier.exportedModifier);
1703
1704        return result.length > 0 ? result.join(",") : ScriptElementKindModifier.none;
1705    }
1706
1707    export function getTypeArgumentOrTypeParameterList(node: Node): NodeArray<Node> | undefined {
1708        if (node.kind === SyntaxKind.TypeReference || node.kind === SyntaxKind.CallExpression) {
1709            return (node as CallExpression).typeArguments;
1710        }
1711
1712        if (isFunctionLike(node) || node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.InterfaceDeclaration) {
1713            return (node as FunctionLikeDeclaration).typeParameters;
1714        }
1715
1716        return undefined;
1717    }
1718
1719    export function isComment(kind: SyntaxKind): boolean {
1720        return kind === SyntaxKind.SingleLineCommentTrivia || kind === SyntaxKind.MultiLineCommentTrivia;
1721    }
1722
1723    export function isStringOrRegularExpressionOrTemplateLiteral(kind: SyntaxKind): boolean {
1724        if (kind === SyntaxKind.StringLiteral
1725            || kind === SyntaxKind.RegularExpressionLiteral
1726            || isTemplateLiteralKind(kind)) {
1727            return true;
1728        }
1729        return false;
1730    }
1731
1732    export function isPunctuation(kind: SyntaxKind): boolean {
1733        return SyntaxKind.FirstPunctuation <= kind && kind <= SyntaxKind.LastPunctuation;
1734    }
1735
1736    export function isInsideTemplateLiteral(node: TemplateLiteralToken, position: number, sourceFile: SourceFile): boolean {
1737        return isTemplateLiteralKind(node.kind)
1738            && (node.getStart(sourceFile) < position && position < node.end) || (!!node.isUnterminated && position === node.end);
1739    }
1740
1741    export function isAccessibilityModifier(kind: SyntaxKind) {
1742        switch (kind) {
1743            case SyntaxKind.PublicKeyword:
1744            case SyntaxKind.PrivateKeyword:
1745            case SyntaxKind.ProtectedKeyword:
1746                return true;
1747        }
1748
1749        return false;
1750    }
1751
1752    export function cloneCompilerOptions(options: CompilerOptions): CompilerOptions {
1753        const result = clone(options);
1754        setConfigFileInOptions(result, options && options.configFile);
1755        return result;
1756    }
1757
1758    export function isArrayLiteralOrObjectLiteralDestructuringPattern(node: Node) {
1759        if (node.kind === SyntaxKind.ArrayLiteralExpression ||
1760            node.kind === SyntaxKind.ObjectLiteralExpression) {
1761            // [a,b,c] from:
1762            // [a, b, c] = someExpression;
1763            if (node.parent.kind === SyntaxKind.BinaryExpression &&
1764                (node.parent as BinaryExpression).left === node &&
1765                (node.parent as BinaryExpression).operatorToken.kind === SyntaxKind.EqualsToken) {
1766                return true;
1767            }
1768
1769            // [a, b, c] from:
1770            // for([a, b, c] of expression)
1771            if (node.parent.kind === SyntaxKind.ForOfStatement &&
1772                (node.parent as ForOfStatement).initializer === node) {
1773                return true;
1774            }
1775
1776            // [a, b, c] of
1777            // [x, [a, b, c] ] = someExpression
1778            // or
1779            // {x, a: {a, b, c} } = someExpression
1780            if (isArrayLiteralOrObjectLiteralDestructuringPattern(node.parent.kind === SyntaxKind.PropertyAssignment ? node.parent.parent : node.parent)) {
1781                return true;
1782            }
1783        }
1784
1785        return false;
1786    }
1787
1788    export function isInReferenceComment(sourceFile: SourceFile, position: number): boolean {
1789        return isInReferenceCommentWorker(sourceFile, position, /*shouldBeReference*/ true);
1790    }
1791
1792    export function isInNonReferenceComment(sourceFile: SourceFile, position: number): boolean {
1793        return isInReferenceCommentWorker(sourceFile, position, /*shouldBeReference*/ false);
1794    }
1795
1796    function isInReferenceCommentWorker(sourceFile: SourceFile, position: number, shouldBeReference: boolean): boolean {
1797        const range = isInComment(sourceFile, position, /*tokenAtPosition*/ undefined);
1798        return !!range && shouldBeReference === tripleSlashDirectivePrefixRegex.test(sourceFile.text.substring(range.pos, range.end));
1799    }
1800
1801    export function getReplacementSpanForContextToken(contextToken: Node | undefined) {
1802        if (!contextToken) return undefined;
1803
1804        switch (contextToken.kind) {
1805            case SyntaxKind.StringLiteral:
1806            case SyntaxKind.NoSubstitutionTemplateLiteral:
1807                return createTextSpanFromStringLiteralLikeContent(contextToken as StringLiteralLike);
1808            default:
1809                return createTextSpanFromNode(contextToken);
1810        }
1811    }
1812
1813    export function createTextSpanFromNode(node: Node, sourceFile?: SourceFile, endNode?: Node): TextSpan {
1814        return createTextSpanFromBounds(node.getStart(sourceFile), (endNode || node).getEnd());
1815    }
1816
1817    export function createTextSpanFromStringLiteralLikeContent(node: StringLiteralLike) {
1818        if (node.isUnterminated) return undefined;
1819        return createTextSpanFromBounds(node.getStart() + 1, node.getEnd() - 1);
1820    }
1821
1822    export function createTextRangeFromNode(node: Node, sourceFile: SourceFile): TextRange {
1823        return createRange(node.getStart(sourceFile), node.end);
1824    }
1825
1826    export function createTextSpanFromRange(range: TextRange): TextSpan {
1827        return createTextSpanFromBounds(range.pos, range.end);
1828    }
1829
1830    export function createTextRangeFromSpan(span: TextSpan): TextRange {
1831        return createRange(span.start, span.start + span.length);
1832    }
1833
1834    export function createTextChangeFromStartLength(start: number, length: number, newText: string): TextChange {
1835        return createTextChange(createTextSpan(start, length), newText);
1836    }
1837
1838    export function createTextChange(span: TextSpan, newText: string): TextChange {
1839        return { span, newText };
1840    }
1841
1842    export const typeKeywords: readonly SyntaxKind[] = [
1843        SyntaxKind.AnyKeyword,
1844        SyntaxKind.AssertsKeyword,
1845        SyntaxKind.BigIntKeyword,
1846        SyntaxKind.BooleanKeyword,
1847        SyntaxKind.FalseKeyword,
1848        SyntaxKind.InferKeyword,
1849        SyntaxKind.KeyOfKeyword,
1850        SyntaxKind.NeverKeyword,
1851        SyntaxKind.NullKeyword,
1852        SyntaxKind.NumberKeyword,
1853        SyntaxKind.ObjectKeyword,
1854        SyntaxKind.ReadonlyKeyword,
1855        SyntaxKind.StringKeyword,
1856        SyntaxKind.SymbolKeyword,
1857        SyntaxKind.TrueKeyword,
1858        SyntaxKind.VoidKeyword,
1859        SyntaxKind.UndefinedKeyword,
1860        SyntaxKind.UniqueKeyword,
1861        SyntaxKind.UnknownKeyword,
1862    ];
1863
1864    export function isTypeKeyword(kind: SyntaxKind): boolean {
1865        return contains(typeKeywords, kind);
1866    }
1867
1868    export function isTypeKeywordToken(node: Node): node is Token<SyntaxKind.TypeKeyword> {
1869        return node.kind === SyntaxKind.TypeKeyword;
1870    }
1871
1872    export function isTypeKeywordTokenOrIdentifier(node: Node) {
1873        return isTypeKeywordToken(node) || isIdentifier(node) && node.text === "type";
1874    }
1875
1876    /** True if the symbol is for an external module, as opposed to a namespace. */
1877    export function isExternalModuleSymbol(moduleSymbol: Symbol): boolean {
1878        return !!(moduleSymbol.flags & SymbolFlags.Module) && moduleSymbol.name.charCodeAt(0) === CharacterCodes.doubleQuote;
1879    }
1880
1881    /** Returns `true` the first time it encounters a node and `false` afterwards. */
1882    export type NodeSeenTracker<T = Node> = (node: T) => boolean;
1883    export function nodeSeenTracker<T extends Node>(): NodeSeenTracker<T> {
1884        const seen: true[] = [];
1885        return node => {
1886            const id = getNodeId(node);
1887            return !seen[id] && (seen[id] = true);
1888        };
1889    }
1890
1891    export function getSnapshotText(snap: IScriptSnapshot): string {
1892        return snap.getText(0, snap.getLength());
1893    }
1894
1895    export function repeatString(str: string, count: number): string {
1896        let result = "";
1897        for (let i = 0; i < count; i++) {
1898            result += str;
1899        }
1900        return result;
1901    }
1902
1903    export function skipConstraint(type: Type): Type {
1904        return type.isTypeParameter() ? type.getConstraint() || type : type;
1905    }
1906
1907    export function getNameFromPropertyName(name: PropertyName): string | undefined {
1908        return name.kind === SyntaxKind.ComputedPropertyName
1909            // treat computed property names where expression is string/numeric literal as just string/numeric literal
1910            ? isStringOrNumericLiteralLike(name.expression) ? name.expression.text : undefined
1911            : isPrivateIdentifier(name) ? idText(name) : getTextOfIdentifierOrLiteral(name);
1912    }
1913
1914    export function programContainsModules(program: Program): boolean {
1915        return program.getSourceFiles().some(s => !s.isDeclarationFile && !program.isSourceFileFromExternalLibrary(s) && !!(s.externalModuleIndicator || s.commonJsModuleIndicator));
1916    }
1917    export function programContainsEsModules(program: Program): boolean {
1918        return program.getSourceFiles().some(s => !s.isDeclarationFile && !program.isSourceFileFromExternalLibrary(s) && !!s.externalModuleIndicator);
1919    }
1920    export function compilerOptionsIndicateEsModules(compilerOptions: CompilerOptions): boolean {
1921        return !!compilerOptions.module || getEmitScriptTarget(compilerOptions) >= ScriptTarget.ES2015 || !!compilerOptions.noEmit;
1922    }
1923
1924    export function createModuleSpecifierResolutionHost(program: Program, host: LanguageServiceHost): ModuleSpecifierResolutionHost {
1925        // Mix in `getSymlinkCache` from Program when host doesn't have it
1926        // in order for non-Project hosts to have a symlinks cache.
1927        return {
1928            fileExists: fileName => program.fileExists(fileName),
1929            getCurrentDirectory: () => host.getCurrentDirectory(),
1930            readFile: maybeBind(host, host.readFile),
1931            useCaseSensitiveFileNames: maybeBind(host, host.useCaseSensitiveFileNames),
1932            getSymlinkCache: maybeBind(host, host.getSymlinkCache) || program.getSymlinkCache,
1933            getModuleSpecifierCache: maybeBind(host, host.getModuleSpecifierCache),
1934            getPackageJsonInfoCache: () => program.getModuleResolutionCache()?.getPackageJsonInfoCache(),
1935            getGlobalTypingsCacheLocation: maybeBind(host, host.getGlobalTypingsCacheLocation),
1936            redirectTargetsMap: program.redirectTargetsMap,
1937            getProjectReferenceRedirect: fileName => program.getProjectReferenceRedirect(fileName),
1938            isSourceOfProjectReferenceRedirect: fileName => program.isSourceOfProjectReferenceRedirect(fileName),
1939            getNearestAncestorDirectoryWithPackageJson: maybeBind(host, host.getNearestAncestorDirectoryWithPackageJson),
1940            getFileIncludeReasons: () => program.getFileIncludeReasons(),
1941        };
1942    }
1943
1944    export function getModuleSpecifierResolverHost(program: Program, host: LanguageServiceHost): SymbolTracker["moduleResolverHost"] {
1945        return {
1946            ...createModuleSpecifierResolutionHost(program, host),
1947            getCommonSourceDirectory: () => program.getCommonSourceDirectory(),
1948        };
1949    }
1950
1951    export function moduleResolutionRespectsExports(moduleResolution: ModuleResolutionKind): boolean {
1952        return moduleResolution >= ModuleResolutionKind.Node16 && moduleResolution <= ModuleResolutionKind.NodeNext;
1953    }
1954
1955    export function moduleResolutionUsesNodeModules(moduleResolution: ModuleResolutionKind): boolean {
1956        return moduleResolution === ModuleResolutionKind.NodeJs || moduleResolution >= ModuleResolutionKind.Node16 && moduleResolution <= ModuleResolutionKind.NodeNext;
1957    }
1958
1959    export function makeImportIfNecessary(defaultImport: Identifier | undefined, namedImports: readonly ImportSpecifier[] | undefined, moduleSpecifier: string, quotePreference: QuotePreference): ImportDeclaration | undefined {
1960        return defaultImport || namedImports && namedImports.length ? makeImport(defaultImport, namedImports, moduleSpecifier, quotePreference) : undefined;
1961    }
1962
1963    export function makeImport(defaultImport: Identifier | undefined, namedImports: readonly ImportSpecifier[] | undefined, moduleSpecifier: string | Expression, quotePreference: QuotePreference, isTypeOnly?: boolean): ImportDeclaration {
1964        return factory.createImportDeclaration(
1965            /*modifiers*/ undefined,
1966            defaultImport || namedImports
1967                ? factory.createImportClause(!!isTypeOnly, defaultImport, namedImports && namedImports.length ? factory.createNamedImports(namedImports) : undefined)
1968                : undefined,
1969            typeof moduleSpecifier === "string" ? makeStringLiteral(moduleSpecifier, quotePreference) : moduleSpecifier,
1970            /*assertClause*/ undefined);
1971    }
1972
1973    export function makeStringLiteral(text: string, quotePreference: QuotePreference): StringLiteral {
1974        return factory.createStringLiteral(text, quotePreference === QuotePreference.Single);
1975    }
1976
1977    export const enum QuotePreference { Single, Double }
1978
1979    export function quotePreferenceFromString(str: StringLiteral, sourceFile: SourceFile): QuotePreference {
1980        return isStringDoubleQuoted(str, sourceFile) ? QuotePreference.Double : QuotePreference.Single;
1981    }
1982
1983    export function getQuotePreference(sourceFile: SourceFile, preferences: UserPreferences): QuotePreference {
1984        if (preferences.quotePreference && preferences.quotePreference !== "auto") {
1985            return preferences.quotePreference === "single" ? QuotePreference.Single : QuotePreference.Double;
1986        }
1987        else {
1988            // ignore synthetic import added when importHelpers: true
1989            const firstModuleSpecifier = sourceFile.imports &&
1990                find(sourceFile.imports, n => isStringLiteral(n) && !nodeIsSynthesized(n.parent)) as StringLiteral;
1991            return firstModuleSpecifier ? quotePreferenceFromString(firstModuleSpecifier, sourceFile) : QuotePreference.Double;
1992        }
1993    }
1994
1995    export function getQuoteFromPreference(qp: QuotePreference): string {
1996        switch (qp) {
1997            case QuotePreference.Single: return "'";
1998            case QuotePreference.Double: return '"';
1999            default: return Debug.assertNever(qp);
2000        }
2001    }
2002
2003    export function symbolNameNoDefault(symbol: Symbol): string | undefined {
2004        const escaped = symbolEscapedNameNoDefault(symbol);
2005        return escaped === undefined ? undefined : unescapeLeadingUnderscores(escaped);
2006    }
2007
2008    export function symbolEscapedNameNoDefault(symbol: Symbol): __String | undefined {
2009        if (symbol.escapedName !== InternalSymbolName.Default) {
2010            return symbol.escapedName;
2011        }
2012
2013        return firstDefined(symbol.declarations, decl => {
2014            const name = getNameOfDeclaration(decl);
2015            return name && name.kind === SyntaxKind.Identifier ? name.escapedText : undefined;
2016        });
2017    }
2018
2019    export function isModuleSpecifierLike(node: Node): node is StringLiteralLike {
2020        return isStringLiteralLike(node) && (
2021            isExternalModuleReference(node.parent) ||
2022            isImportDeclaration(node.parent) ||
2023            isRequireCall(node.parent, /*requireStringLiteralLikeArgument*/ false) && node.parent.arguments[0] === node ||
2024            isImportCall(node.parent) && node.parent.arguments[0] === node);
2025    }
2026
2027    export type ObjectBindingElementWithoutPropertyName = BindingElement & { name: Identifier };
2028
2029    export function isObjectBindingElementWithoutPropertyName(bindingElement: Node): bindingElement is ObjectBindingElementWithoutPropertyName {
2030        return isBindingElement(bindingElement) &&
2031            isObjectBindingPattern(bindingElement.parent) &&
2032            isIdentifier(bindingElement.name) &&
2033            !bindingElement.propertyName;
2034    }
2035
2036    export function getPropertySymbolFromBindingElement(checker: TypeChecker, bindingElement: ObjectBindingElementWithoutPropertyName): Symbol | undefined {
2037        const typeOfPattern = checker.getTypeAtLocation(bindingElement.parent);
2038        return typeOfPattern && checker.getPropertyOfType(typeOfPattern, bindingElement.name.text);
2039    }
2040
2041    export function getParentNodeInSpan(node: Node | undefined, file: SourceFile, span: TextSpan): Node | undefined {
2042        if (!node) return undefined;
2043
2044        while (node.parent) {
2045            if (isSourceFile(node.parent) || !spanContainsNode(span, node.parent, file)) {
2046                return node;
2047            }
2048
2049            node = node.parent;
2050        }
2051    }
2052
2053    function spanContainsNode(span: TextSpan, node: Node, file: SourceFile): boolean {
2054        return textSpanContainsPosition(span, node.getStart(file)) &&
2055            node.getEnd() <= textSpanEnd(span);
2056    }
2057
2058    export function findModifier(node: Node, kind: Modifier["kind"]): Modifier | undefined {
2059        return canHaveModifiers(node) ? find(node.modifiers, (m): m is Modifier => m.kind === kind) : undefined;
2060    }
2061
2062    export function insertImports(changes: textChanges.ChangeTracker, sourceFile: SourceFile, imports: AnyImportOrRequireStatement | readonly AnyImportOrRequireStatement[], blankLineBetween: boolean): void {
2063        const decl = isArray(imports) ? imports[0] : imports;
2064        const importKindPredicate: (node: Node) => node is AnyImportOrRequireStatement = decl.kind === SyntaxKind.VariableStatement ? isRequireVariableStatement : isAnyImportSyntax;
2065        const existingImportStatements = filter(sourceFile.statements, importKindPredicate);
2066        const sortedNewImports = isArray(imports) ? stableSort(imports, OrganizeImports.compareImportsOrRequireStatements) : [imports];
2067        if (!existingImportStatements.length) {
2068            changes.insertNodesAtTopOfFile(sourceFile, sortedNewImports, blankLineBetween);
2069        }
2070        else if (existingImportStatements && OrganizeImports.importsAreSorted(existingImportStatements)) {
2071            for (const newImport of sortedNewImports) {
2072                const insertionIndex = OrganizeImports.getImportDeclarationInsertionIndex(existingImportStatements, newImport);
2073                if (insertionIndex === 0) {
2074                    // If the first import is top-of-file, insert after the leading comment which is likely the header.
2075                    const options = existingImportStatements[0] === sourceFile.statements[0] ?
2076                    { leadingTriviaOption: textChanges.LeadingTriviaOption.Exclude } : {};
2077                    changes.insertNodeBefore(sourceFile, existingImportStatements[0], newImport, /*blankLineBetween*/ false, options);
2078                }
2079                else {
2080                    const prevImport = existingImportStatements[insertionIndex - 1];
2081                    changes.insertNodeAfter(sourceFile, prevImport, newImport);
2082                }
2083            }
2084        }
2085        else {
2086            const lastExistingImport = lastOrUndefined(existingImportStatements);
2087            if (lastExistingImport) {
2088                changes.insertNodesAfter(sourceFile, lastExistingImport, sortedNewImports);
2089            }
2090            else {
2091                changes.insertNodesAtTopOfFile(sourceFile, sortedNewImports, blankLineBetween);
2092            }
2093        }
2094    }
2095
2096    export function getTypeKeywordOfTypeOnlyImport(importClause: ImportClause, sourceFile: SourceFile): Token<SyntaxKind.TypeKeyword> {
2097        Debug.assert(importClause.isTypeOnly);
2098        return cast(importClause.getChildAt(0, sourceFile), isTypeKeywordToken);
2099    }
2100
2101    export function textSpansEqual(a: TextSpan | undefined, b: TextSpan | undefined): boolean {
2102        return !!a && !!b && a.start === b.start && a.length === b.length;
2103    }
2104    export function documentSpansEqual(a: DocumentSpan, b: DocumentSpan): boolean {
2105        return a.fileName === b.fileName && textSpansEqual(a.textSpan, b.textSpan);
2106    }
2107
2108    /**
2109     * Iterates through 'array' by index and performs the callback on each element of array until the callback
2110     * returns a truthy value, then returns that value.
2111     * If no such value is found, the callback is applied to each element of array and undefined is returned.
2112     */
2113    export function forEachUnique<T, U>(array: readonly T[] | undefined, callback: (element: T, index: number) => U): U | undefined {
2114        if (array) {
2115            for (let i = 0; i < array.length; i++) {
2116                if (array.indexOf(array[i]) === i) {
2117                    const result = callback(array[i], i);
2118                    if (result) {
2119                        return result;
2120                    }
2121                }
2122            }
2123        }
2124        return undefined;
2125    }
2126
2127    export function isTextWhiteSpaceLike(text: string, startPos: number, endPos: number): boolean {
2128        for (let i = startPos; i < endPos; i++) {
2129            if (!isWhiteSpaceLike(text.charCodeAt(i))) {
2130                return false;
2131            }
2132        }
2133
2134        return true;
2135    }
2136
2137    export function getMappedLocation(location: DocumentPosition, sourceMapper: SourceMapper, fileExists: ((path: string) => boolean) | undefined): DocumentPosition | undefined {
2138        const mapsTo = sourceMapper.tryGetSourcePosition(location);
2139        return mapsTo && (!fileExists || fileExists(normalizePath(mapsTo.fileName)) ? mapsTo : undefined);
2140    }
2141
2142    export function getMappedDocumentSpan(documentSpan: DocumentSpan, sourceMapper: SourceMapper, fileExists?: (path: string) => boolean): DocumentSpan | undefined {
2143        const { fileName, textSpan } = documentSpan;
2144        const newPosition = getMappedLocation({ fileName, pos: textSpan.start }, sourceMapper, fileExists);
2145        if (!newPosition) return undefined;
2146        const newEndPosition = getMappedLocation({ fileName, pos: textSpan.start + textSpan.length }, sourceMapper, fileExists);
2147        const newLength = newEndPosition
2148            ? newEndPosition.pos - newPosition.pos
2149            : textSpan.length; // This shouldn't happen
2150        return {
2151            fileName: newPosition.fileName,
2152            textSpan: {
2153                start: newPosition.pos,
2154                length: newLength,
2155            },
2156            originalFileName: documentSpan.fileName,
2157            originalTextSpan: documentSpan.textSpan,
2158            contextSpan: getMappedContextSpan(documentSpan, sourceMapper, fileExists),
2159            originalContextSpan: documentSpan.contextSpan
2160        };
2161    }
2162
2163    export function getMappedContextSpan(documentSpan: DocumentSpan, sourceMapper: SourceMapper, fileExists?: (path: string) => boolean): TextSpan | undefined {
2164        const contextSpanStart = documentSpan.contextSpan && getMappedLocation(
2165            { fileName: documentSpan.fileName, pos: documentSpan.contextSpan.start },
2166            sourceMapper,
2167            fileExists
2168        );
2169        const contextSpanEnd = documentSpan.contextSpan && getMappedLocation(
2170            { fileName: documentSpan.fileName, pos: documentSpan.contextSpan.start + documentSpan.contextSpan.length },
2171            sourceMapper,
2172            fileExists
2173        );
2174        return contextSpanStart && contextSpanEnd ?
2175            { start: contextSpanStart.pos, length: contextSpanEnd.pos - contextSpanStart.pos } :
2176            undefined;
2177    }
2178
2179    // #endregion
2180
2181    // Display-part writer helpers
2182    // #region
2183    export function isFirstDeclarationOfSymbolParameter(symbol: Symbol) {
2184        const declaration = symbol.declarations ? firstOrUndefined(symbol.declarations) : undefined;
2185        return !!findAncestor(declaration, n =>
2186            isParameter(n) ? true : isBindingElement(n) || isObjectBindingPattern(n) || isArrayBindingPattern(n) ? false : "quit");
2187    }
2188
2189    const displayPartWriter = getDisplayPartWriter();
2190    function getDisplayPartWriter(): DisplayPartsSymbolWriter {
2191        const absoluteMaximumLength = defaultMaximumTruncationLength * 10; // A hard cutoff to avoid overloading the messaging channel in worst-case scenarios
2192        let displayParts: SymbolDisplayPart[];
2193        let lineStart: boolean;
2194        let indent: number;
2195        let length: number;
2196
2197        resetWriter();
2198        const unknownWrite = (text: string) => writeKind(text, SymbolDisplayPartKind.text);
2199        return {
2200            displayParts: () => {
2201                const finalText = displayParts.length && displayParts[displayParts.length - 1].text;
2202                if (length > absoluteMaximumLength && finalText && finalText !== "...") {
2203                    if (!isWhiteSpaceLike(finalText.charCodeAt(finalText.length - 1))) {
2204                        displayParts.push(displayPart(" ", SymbolDisplayPartKind.space));
2205                    }
2206                    displayParts.push(displayPart("...", SymbolDisplayPartKind.punctuation));
2207                }
2208                return displayParts;
2209            },
2210            writeKeyword: text => writeKind(text, SymbolDisplayPartKind.keyword),
2211            writeOperator: text => writeKind(text, SymbolDisplayPartKind.operator),
2212            writePunctuation: text => writeKind(text, SymbolDisplayPartKind.punctuation),
2213            writeTrailingSemicolon: text => writeKind(text, SymbolDisplayPartKind.punctuation),
2214            writeSpace: text => writeKind(text, SymbolDisplayPartKind.space),
2215            writeStringLiteral: text => writeKind(text, SymbolDisplayPartKind.stringLiteral),
2216            writeParameter: text => writeKind(text, SymbolDisplayPartKind.parameterName),
2217            writeProperty: text => writeKind(text, SymbolDisplayPartKind.propertyName),
2218            writeLiteral: text => writeKind(text, SymbolDisplayPartKind.stringLiteral),
2219            writeSymbol,
2220            writeLine,
2221            write: unknownWrite,
2222            writeComment: unknownWrite,
2223            getText: () => "",
2224            getTextPos: () => 0,
2225            getColumn: () => 0,
2226            getLine: () => 0,
2227            isAtStartOfLine: () => false,
2228            hasTrailingWhitespace: () => false,
2229            hasTrailingComment: () => false,
2230            rawWrite: notImplemented,
2231            getIndent: () => indent,
2232            increaseIndent: () => { indent++; },
2233            decreaseIndent: () => { indent--; },
2234            clear: resetWriter,
2235            trackSymbol: () => false,
2236            reportInaccessibleThisError: noop,
2237            reportInaccessibleUniqueSymbolError: noop,
2238            reportPrivateInBaseOfClassExpression: noop,
2239        };
2240
2241        function writeIndent() {
2242            if (length > absoluteMaximumLength) return;
2243            if (lineStart) {
2244                const indentString = getIndentString(indent);
2245                if (indentString) {
2246                    length += indentString.length;
2247                    displayParts.push(displayPart(indentString, SymbolDisplayPartKind.space));
2248                }
2249                lineStart = false;
2250            }
2251        }
2252
2253        function writeKind(text: string, kind: SymbolDisplayPartKind) {
2254            if (length > absoluteMaximumLength) return;
2255            writeIndent();
2256            length += text.length;
2257            displayParts.push(displayPart(text, kind));
2258        }
2259
2260        function writeSymbol(text: string, symbol: Symbol) {
2261            if (length > absoluteMaximumLength) return;
2262            writeIndent();
2263            length += text.length;
2264            displayParts.push(symbolPart(text, symbol));
2265        }
2266
2267        function writeLine() {
2268            if (length > absoluteMaximumLength) return;
2269            length += 1;
2270            displayParts.push(lineBreakPart());
2271            lineStart = true;
2272        }
2273
2274        function resetWriter() {
2275            displayParts = [];
2276            lineStart = true;
2277            indent = 0;
2278            length = 0;
2279        }
2280    }
2281
2282    export function symbolPart(text: string, symbol: Symbol) {
2283        return displayPart(text, displayPartKind(symbol));
2284
2285        function displayPartKind(symbol: Symbol): SymbolDisplayPartKind {
2286            const flags = symbol.flags;
2287
2288            if (flags & SymbolFlags.Variable) {
2289                return isFirstDeclarationOfSymbolParameter(symbol) ? SymbolDisplayPartKind.parameterName : SymbolDisplayPartKind.localName;
2290            }
2291            if (flags & SymbolFlags.Property) return SymbolDisplayPartKind.propertyName;
2292            if (flags & SymbolFlags.GetAccessor) return SymbolDisplayPartKind.propertyName;
2293            if (flags & SymbolFlags.SetAccessor) return SymbolDisplayPartKind.propertyName;
2294            if (flags & SymbolFlags.EnumMember) return SymbolDisplayPartKind.enumMemberName;
2295            if (flags & SymbolFlags.Function) return SymbolDisplayPartKind.functionName;
2296            if (flags & SymbolFlags.Class) return SymbolDisplayPartKind.className;
2297            if (flags & SymbolFlags.Interface) return SymbolDisplayPartKind.interfaceName;
2298            if (flags & SymbolFlags.Enum) return SymbolDisplayPartKind.enumName;
2299            if (flags & SymbolFlags.Module) return SymbolDisplayPartKind.moduleName;
2300            if (flags & SymbolFlags.Method) return SymbolDisplayPartKind.methodName;
2301            if (flags & SymbolFlags.TypeParameter) return SymbolDisplayPartKind.typeParameterName;
2302            if (flags & SymbolFlags.TypeAlias) return SymbolDisplayPartKind.aliasName;
2303            if (flags & SymbolFlags.Alias) return SymbolDisplayPartKind.aliasName;
2304
2305            return SymbolDisplayPartKind.text;
2306        }
2307    }
2308
2309    export function displayPart(text: string, kind: SymbolDisplayPartKind): SymbolDisplayPart {
2310        return { text, kind: SymbolDisplayPartKind[kind] };
2311    }
2312
2313    export function spacePart() {
2314        return displayPart(" ", SymbolDisplayPartKind.space);
2315    }
2316
2317    export function keywordPart(kind: SyntaxKind) {
2318        return displayPart(tokenToString(kind)!, SymbolDisplayPartKind.keyword);
2319    }
2320
2321    export function punctuationPart(kind: SyntaxKind) {
2322        return displayPart(tokenToString(kind)!, SymbolDisplayPartKind.punctuation);
2323    }
2324
2325    export function operatorPart(kind: SyntaxKind) {
2326        return displayPart(tokenToString(kind)!, SymbolDisplayPartKind.operator);
2327    }
2328
2329    export function parameterNamePart(text: string) {
2330        return displayPart(text, SymbolDisplayPartKind.parameterName);
2331    }
2332
2333    export function propertyNamePart(text: string) {
2334        return displayPart(text, SymbolDisplayPartKind.propertyName);
2335    }
2336
2337    export function textOrKeywordPart(text: string) {
2338        const kind = stringToToken(text);
2339        return kind === undefined
2340            ? textPart(text)
2341            : keywordPart(kind);
2342    }
2343
2344    export function textPart(text: string) {
2345        return displayPart(text, SymbolDisplayPartKind.text);
2346    }
2347
2348    export function typeAliasNamePart(text: string) {
2349        return displayPart(text, SymbolDisplayPartKind.aliasName);
2350    }
2351
2352    export function typeParameterNamePart(text: string) {
2353        return displayPart(text, SymbolDisplayPartKind.typeParameterName);
2354    }
2355
2356    export function linkTextPart(text: string) {
2357        return displayPart(text, SymbolDisplayPartKind.linkText);
2358    }
2359
2360    export function linkNamePart(text: string, target: Declaration): JSDocLinkDisplayPart {
2361        return {
2362            text,
2363            kind: SymbolDisplayPartKind[SymbolDisplayPartKind.linkName],
2364            target: {
2365                fileName: getSourceFileOfNode(target).fileName,
2366                textSpan: createTextSpanFromNode(target),
2367            },
2368        };
2369    }
2370
2371    export function linkPart(text: string) {
2372        return displayPart(text, SymbolDisplayPartKind.link);
2373    }
2374
2375    export function buildLinkParts(link: JSDocLink | JSDocLinkCode | JSDocLinkPlain, checker?: TypeChecker): SymbolDisplayPart[] {
2376        const prefix = isJSDocLink(link) ? "link"
2377            : isJSDocLinkCode(link) ? "linkcode"
2378            : "linkplain";
2379        const parts = [linkPart(`{@${prefix} `)];
2380        if (!link.name) {
2381            if (link.text) {
2382                parts.push(linkTextPart(link.text));
2383            }
2384        }
2385        else {
2386            const symbol = checker?.getSymbolAtLocation(link.name);
2387            const suffix = findLinkNameEnd(link.text);
2388            const name = getTextOfNode(link.name) + link.text.slice(0, suffix);
2389            const text = skipSeparatorFromLinkText(link.text.slice(suffix));
2390            const decl = symbol?.valueDeclaration || symbol?.declarations?.[0];
2391            if (decl) {
2392                parts.push(linkNamePart(name, decl));
2393                if (text) parts.push(linkTextPart(text));
2394            }
2395            else {
2396                parts.push(linkTextPart(name + (suffix || text.indexOf("://") === 0 ? "" : " ") + text));
2397            }
2398        }
2399        parts.push(linkPart("}"));
2400        return parts;
2401    }
2402
2403    function skipSeparatorFromLinkText(text: string) {
2404        let pos = 0;
2405        if (text.charCodeAt(pos++) === CharacterCodes.bar) {
2406            while (pos < text.length && text.charCodeAt(pos) === CharacterCodes.space) pos++;
2407            return text.slice(pos);
2408        }
2409        return text;
2410    }
2411
2412    function findLinkNameEnd(text: string) {
2413        if (text.indexOf("()") === 0) return 2;
2414        if (text[0] !== "<") return 0;
2415        let brackets = 0;
2416        let i = 0;
2417        while (i < text.length) {
2418            if (text[i] === "<") brackets++;
2419            if (text[i] === ">") brackets--;
2420            i++;
2421            if (!brackets) return i;
2422        }
2423        return 0;
2424    }
2425
2426    const carriageReturnLineFeed = "\r\n";
2427    /**
2428     * The default is CRLF.
2429     */
2430    export function getNewLineOrDefaultFromHost(host: FormattingHost, formatSettings?: FormatCodeSettings) {
2431        return formatSettings?.newLineCharacter ||
2432            host.getNewLine?.() ||
2433            carriageReturnLineFeed;
2434    }
2435
2436    export function lineBreakPart() {
2437        return displayPart("\n", SymbolDisplayPartKind.lineBreak);
2438    }
2439
2440    export function mapToDisplayParts(writeDisplayParts: (writer: DisplayPartsSymbolWriter) => void): SymbolDisplayPart[] {
2441        try {
2442            writeDisplayParts(displayPartWriter);
2443            return displayPartWriter.displayParts();
2444        }
2445        finally {
2446            displayPartWriter.clear();
2447        }
2448    }
2449
2450    export function typeToDisplayParts(typechecker: TypeChecker, type: Type, enclosingDeclaration?: Node, flags: TypeFormatFlags = TypeFormatFlags.None): SymbolDisplayPart[] {
2451        return mapToDisplayParts(writer => {
2452            typechecker.writeType(type, enclosingDeclaration, flags | TypeFormatFlags.MultilineObjectLiterals | TypeFormatFlags.UseAliasDefinedOutsideCurrentScope, writer);
2453        });
2454    }
2455
2456    export function symbolToDisplayParts(typeChecker: TypeChecker, symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags, flags: SymbolFormatFlags = SymbolFormatFlags.None): SymbolDisplayPart[] {
2457        return mapToDisplayParts(writer => {
2458            typeChecker.writeSymbol(symbol, enclosingDeclaration, meaning, flags | SymbolFormatFlags.UseAliasDefinedOutsideCurrentScope, writer);
2459        });
2460    }
2461
2462    export function signatureToDisplayParts(typechecker: TypeChecker, signature: Signature, enclosingDeclaration?: Node, flags: TypeFormatFlags = TypeFormatFlags.None): SymbolDisplayPart[] {
2463        flags |= TypeFormatFlags.UseAliasDefinedOutsideCurrentScope | TypeFormatFlags.MultilineObjectLiterals | TypeFormatFlags.WriteTypeArgumentsOfSignature | TypeFormatFlags.OmitParameterModifiers;
2464        return mapToDisplayParts(writer => {
2465            typechecker.writeSignature(signature, enclosingDeclaration, flags, /*signatureKind*/ undefined, writer);
2466        });
2467    }
2468
2469    export function nodeToDisplayParts(node: Node, enclosingDeclaration: Node): SymbolDisplayPart[] {
2470        const file = enclosingDeclaration.getSourceFile();
2471        return mapToDisplayParts(writer => {
2472            const printer = createPrinter({ removeComments: true, omitTrailingSemicolon: true });
2473            printer.writeNode(EmitHint.Unspecified, node, file, writer);
2474        });
2475    }
2476
2477    export function isImportOrExportSpecifierName(location: Node): location is Identifier {
2478        return !!location.parent && isImportOrExportSpecifier(location.parent) && location.parent.propertyName === location;
2479    }
2480
2481    export function getScriptKind(fileName: string, host: LanguageServiceHost): ScriptKind {
2482        // First check to see if the script kind was specified by the host. Chances are the host
2483        // may override the default script kind for the file extension.
2484        return ensureScriptKind(fileName, host.getScriptKind && host.getScriptKind(fileName));
2485    }
2486
2487    export function getSymbolTarget(symbol: Symbol, checker: TypeChecker): Symbol {
2488        let next: Symbol = symbol;
2489        while (isAliasSymbol(next) || (isTransientSymbol(next) && next.target)) {
2490            if (isTransientSymbol(next) && next.target) {
2491                next = next.target;
2492            }
2493            else {
2494                next = skipAlias(next, checker);
2495            }
2496        }
2497        return next;
2498    }
2499
2500    function isTransientSymbol(symbol: Symbol): symbol is TransientSymbol {
2501        return (symbol.flags & SymbolFlags.Transient) !== 0;
2502    }
2503
2504    function isAliasSymbol(symbol: Symbol): boolean {
2505        return (symbol.flags & SymbolFlags.Alias) !== 0;
2506    }
2507
2508    export function getUniqueSymbolId(symbol: Symbol, checker: TypeChecker) {
2509        return getSymbolId(skipAlias(symbol, checker));
2510    }
2511
2512    export function getFirstNonSpaceCharacterPosition(text: string, position: number) {
2513        while (isWhiteSpaceLike(text.charCodeAt(position))) {
2514            position += 1;
2515        }
2516        return position;
2517    }
2518
2519    export function getPrecedingNonSpaceCharacterPosition(text: string, position: number) {
2520        while (position > -1 && isWhiteSpaceSingleLine(text.charCodeAt(position))) {
2521            position -= 1;
2522        }
2523        return position + 1;
2524    }
2525
2526    /**
2527     * Creates a deep, memberwise clone of a node with no source map location.
2528     *
2529     * WARNING: This is an expensive operation and is only intended to be used in refactorings
2530     * and code fixes (because those are triggered by explicit user actions).
2531     */
2532    export function getSynthesizedDeepClone<T extends Node | undefined>(node: T, includeTrivia = true): T {
2533        const clone = node && getSynthesizedDeepCloneWorker(node);
2534        if (clone && !includeTrivia) suppressLeadingAndTrailingTrivia(clone);
2535        return clone;
2536    }
2537
2538    export function getSynthesizedDeepCloneWithReplacements<T extends Node>(
2539        node: T,
2540        includeTrivia: boolean,
2541        replaceNode: (node: Node) => Node | undefined
2542    ): T {
2543        let clone = replaceNode(node);
2544        if (clone) {
2545            setOriginalNode(clone, node);
2546        }
2547        else {
2548            clone = getSynthesizedDeepCloneWorker(node as NonNullable<T>, replaceNode);
2549        }
2550
2551        if (clone && !includeTrivia) suppressLeadingAndTrailingTrivia(clone);
2552        return clone as T;
2553    }
2554
2555    function getSynthesizedDeepCloneWorker<T extends Node>(node: T, replaceNode?: (node: Node) => Node | undefined): T {
2556        const nodeClone: (n: T) => T = replaceNode
2557            ? n => getSynthesizedDeepCloneWithReplacements(n, /*includeTrivia*/ true, replaceNode)
2558            : getSynthesizedDeepClone;
2559        const nodesClone: (ns: NodeArray<T>) => NodeArray<T> = replaceNode
2560            ? ns => ns && getSynthesizedDeepClonesWithReplacements(ns, /*includeTrivia*/ true, replaceNode)
2561            : ns => ns && getSynthesizedDeepClones(ns);
2562        const visited =
2563            visitEachChild(node, nodeClone, nullTransformationContext, nodesClone, nodeClone);
2564
2565        if (visited === node) {
2566            // This only happens for leaf nodes - internal nodes always see their children change.
2567            const clone =
2568                isStringLiteral(node) ? setOriginalNode(factory.createStringLiteralFromNode(node), node) as Node as T :
2569                    isNumericLiteral(node) ? setOriginalNode(factory.createNumericLiteral(node.text, node.numericLiteralFlags), node) as Node as T :
2570                        factory.cloneNode(node);
2571            return setTextRange(clone, node);
2572        }
2573
2574        // PERF: As an optimization, rather than calling factory.cloneNode, we'll update
2575        // the new node created by visitEachChild with the extra changes factory.cloneNode
2576        // would have made.
2577        (visited as Mutable<T>).parent = undefined!;
2578        return visited;
2579    }
2580
2581    export function getSynthesizedDeepClones<T extends Node>(nodes: NodeArray<T>, includeTrivia?: boolean): NodeArray<T>;
2582    export function getSynthesizedDeepClones<T extends Node>(nodes: NodeArray<T> | undefined, includeTrivia?: boolean): NodeArray<T> | undefined;
2583    export function getSynthesizedDeepClones<T extends Node>(nodes: NodeArray<T> | undefined, includeTrivia = true): NodeArray<T> | undefined {
2584        return nodes && factory.createNodeArray(nodes.map(n => getSynthesizedDeepClone(n, includeTrivia)), nodes.hasTrailingComma);
2585    }
2586
2587    export function getSynthesizedDeepClonesWithReplacements<T extends Node>(
2588        nodes: NodeArray<T>,
2589        includeTrivia: boolean,
2590        replaceNode: (node: Node) => Node | undefined
2591    ): NodeArray<T> {
2592        return factory.createNodeArray(nodes.map(n => getSynthesizedDeepCloneWithReplacements(n, includeTrivia, replaceNode)), nodes.hasTrailingComma);
2593    }
2594
2595    /**
2596     * Sets EmitFlags to suppress leading and trailing trivia on the node.
2597     */
2598    export function suppressLeadingAndTrailingTrivia(node: Node) {
2599        suppressLeadingTrivia(node);
2600        suppressTrailingTrivia(node);
2601    }
2602
2603    /**
2604     * Sets EmitFlags to suppress leading trivia on the node.
2605     */
2606    export function suppressLeadingTrivia(node: Node) {
2607        addEmitFlagsRecursively(node, EmitFlags.NoLeadingComments, getFirstChild);
2608    }
2609
2610    /**
2611     * Sets EmitFlags to suppress trailing trivia on the node.
2612     */
2613    export function suppressTrailingTrivia(node: Node) {
2614        addEmitFlagsRecursively(node, EmitFlags.NoTrailingComments, getLastChild);
2615    }
2616
2617    export function copyComments(sourceNode: Node, targetNode: Node) {
2618        const sourceFile = sourceNode.getSourceFile();
2619        const text = sourceFile.text;
2620        if (hasLeadingLineBreak(sourceNode, text)) {
2621            copyLeadingComments(sourceNode, targetNode, sourceFile);
2622        }
2623        else {
2624            copyTrailingAsLeadingComments(sourceNode, targetNode, sourceFile);
2625        }
2626        copyTrailingComments(sourceNode, targetNode, sourceFile);
2627    }
2628
2629    function hasLeadingLineBreak(node: Node, text: string) {
2630        const start = node.getFullStart();
2631        const end = node.getStart();
2632        for (let i = start; i < end; i++) {
2633            if (text.charCodeAt(i) === CharacterCodes.lineFeed) return true;
2634        }
2635        return false;
2636    }
2637
2638    function addEmitFlagsRecursively(node: Node, flag: EmitFlags, getChild: (n: Node) => Node | undefined) {
2639        addEmitFlags(node, flag);
2640        const child = getChild(node);
2641        if (child) addEmitFlagsRecursively(child, flag, getChild);
2642    }
2643
2644    function getFirstChild(node: Node): Node | undefined {
2645        return node.forEachChild(child => child);
2646    }
2647
2648    export function getUniqueName(baseName: string, sourceFile: SourceFile): string {
2649        let nameText = baseName;
2650        for (let i = 1; !isFileLevelUniqueName(sourceFile, nameText); i++) {
2651            nameText = `${baseName}_${i}`;
2652        }
2653        return nameText;
2654    }
2655
2656    /**
2657     * @return The index of the (only) reference to the extracted symbol.  We want the cursor
2658     * to be on the reference, rather than the declaration, because it's closer to where the
2659     * user was before extracting it.
2660     */
2661    export function getRenameLocation(edits: readonly FileTextChanges[], renameFilename: string, name: string, preferLastLocation: boolean): number {
2662        let delta = 0;
2663        let lastPos = -1;
2664        for (const { fileName, textChanges } of edits) {
2665            Debug.assert(fileName === renameFilename);
2666            for (const change of textChanges) {
2667                const { span, newText } = change;
2668                const index = indexInTextChange(newText, escapeString(name));
2669                if (index !== -1) {
2670                    lastPos = span.start + delta + index;
2671
2672                    // If the reference comes first, return immediately.
2673                    if (!preferLastLocation) {
2674                        return lastPos;
2675                    }
2676                }
2677                delta += newText.length - span.length;
2678            }
2679        }
2680
2681        // If the declaration comes first, return the position of the last occurrence.
2682        Debug.assert(preferLastLocation);
2683        Debug.assert(lastPos >= 0);
2684        return lastPos;
2685    }
2686
2687    export function copyLeadingComments(sourceNode: Node, targetNode: Node, sourceFile: SourceFile, commentKind?: CommentKind, hasTrailingNewLine?: boolean) {
2688        forEachLeadingCommentRange(sourceFile.text, sourceNode.pos, getAddCommentsFunction(targetNode, sourceFile, commentKind, hasTrailingNewLine, addSyntheticLeadingComment));
2689    }
2690
2691
2692    export function copyTrailingComments(sourceNode: Node, targetNode: Node, sourceFile: SourceFile, commentKind?: CommentKind, hasTrailingNewLine?: boolean) {
2693        forEachTrailingCommentRange(sourceFile.text, sourceNode.end, getAddCommentsFunction(targetNode, sourceFile, commentKind, hasTrailingNewLine, addSyntheticTrailingComment));
2694    }
2695
2696    /**
2697     * This function copies the trailing comments for the token that comes before `sourceNode`, as leading comments of `targetNode`.
2698     * This is useful because sometimes a comment that refers to `sourceNode` will be a leading comment for `sourceNode`, according to the
2699     * notion of trivia ownership, and instead will be a trailing comment for the token before `sourceNode`, e.g.:
2700     * `function foo(\* not leading comment for a *\ a: string) {}`
2701     * The comment refers to `a` but belongs to the `(` token, but we might want to copy it.
2702     */
2703    export function copyTrailingAsLeadingComments(sourceNode: Node, targetNode: Node, sourceFile: SourceFile, commentKind?: CommentKind, hasTrailingNewLine?: boolean) {
2704        forEachTrailingCommentRange(sourceFile.text, sourceNode.pos, getAddCommentsFunction(targetNode, sourceFile, commentKind, hasTrailingNewLine, addSyntheticLeadingComment));
2705    }
2706
2707    function getAddCommentsFunction(targetNode: Node, sourceFile: SourceFile, commentKind: CommentKind | undefined, hasTrailingNewLine: boolean | undefined, cb: (node: Node, kind: CommentKind, text: string, hasTrailingNewLine?: boolean) => void) {
2708        return (pos: number, end: number, kind: CommentKind, htnl: boolean) => {
2709            if (kind === SyntaxKind.MultiLineCommentTrivia) {
2710                // Remove leading /*
2711                pos += 2;
2712                // Remove trailing */
2713                end -= 2;
2714            }
2715            else {
2716                // Remove leading //
2717                pos += 2;
2718            }
2719            cb(targetNode, commentKind || kind, sourceFile.text.slice(pos, end), hasTrailingNewLine !== undefined ? hasTrailingNewLine : htnl);
2720        };
2721    }
2722
2723    function indexInTextChange(change: string, name: string): number {
2724        if (startsWith(change, name)) return 0;
2725        // Add a " " to avoid references inside words
2726        let idx = change.indexOf(" " + name);
2727        if (idx === -1) idx = change.indexOf("." + name);
2728        if (idx === -1) idx = change.indexOf('"' + name);
2729        return idx === -1 ? -1 : idx + 1;
2730    }
2731
2732    /* @internal */
2733    export function needsParentheses(expression: Expression): boolean {
2734        return isBinaryExpression(expression) && expression.operatorToken.kind === SyntaxKind.CommaToken
2735            || isObjectLiteralExpression(expression)
2736            || isAsExpression(expression) && isObjectLiteralExpression(expression.expression);
2737    }
2738
2739    export function getContextualTypeFromParent(node: Expression, checker: TypeChecker): Type | undefined {
2740        const { parent } = node;
2741        switch (parent.kind) {
2742            case SyntaxKind.NewExpression:
2743                return checker.getContextualType(parent as NewExpression);
2744            case SyntaxKind.BinaryExpression: {
2745                const { left, operatorToken, right } = parent as BinaryExpression;
2746                return isEqualityOperatorKind(operatorToken.kind)
2747                    ? checker.getTypeAtLocation(node === right ? left : right)
2748                    : checker.getContextualType(node);
2749            }
2750            case SyntaxKind.CaseClause:
2751                return (parent as CaseClause).expression === node ? getSwitchedType(parent as CaseClause, checker) : undefined;
2752            default:
2753                return checker.getContextualType(node);
2754        }
2755    }
2756
2757    export function quote(sourceFile: SourceFile, preferences: UserPreferences, text: string): string {
2758        // Editors can pass in undefined or empty string - we want to infer the preference in those cases.
2759        const quotePreference = getQuotePreference(sourceFile, preferences);
2760        const quoted = JSON.stringify(text);
2761        return quotePreference === QuotePreference.Single ? `'${stripQuotes(quoted).replace(/'/g, "\\'").replace(/\\"/g, '"')}'` : quoted;
2762    }
2763
2764    export function isEqualityOperatorKind(kind: SyntaxKind): kind is EqualityOperator {
2765        switch (kind) {
2766            case SyntaxKind.EqualsEqualsEqualsToken:
2767            case SyntaxKind.EqualsEqualsToken:
2768            case SyntaxKind.ExclamationEqualsEqualsToken:
2769            case SyntaxKind.ExclamationEqualsToken:
2770                return true;
2771            default:
2772                return false;
2773        }
2774    }
2775
2776    export function isStringLiteralOrTemplate(node: Node): node is StringLiteralLike | TemplateExpression | TaggedTemplateExpression {
2777        switch (node.kind) {
2778            case SyntaxKind.StringLiteral:
2779            case SyntaxKind.NoSubstitutionTemplateLiteral:
2780            case SyntaxKind.TemplateExpression:
2781            case SyntaxKind.TaggedTemplateExpression:
2782                return true;
2783            default:
2784                return false;
2785        }
2786    }
2787
2788    export function hasIndexSignature(type: Type): boolean {
2789        return !!type.getStringIndexType() || !!type.getNumberIndexType();
2790    }
2791
2792    export function getSwitchedType(caseClause: CaseClause, checker: TypeChecker): Type | undefined {
2793        return checker.getTypeAtLocation(caseClause.parent.parent.expression);
2794    }
2795
2796    export const ANONYMOUS = "anonymous function";
2797
2798    export function getTypeNodeIfAccessible(type: Type, enclosingScope: Node, program: Program, host: LanguageServiceHost): TypeNode | undefined {
2799        const checker = program.getTypeChecker();
2800        let typeIsAccessible = true;
2801        const notAccessible = () => typeIsAccessible = false;
2802        const res = checker.typeToTypeNode(type, enclosingScope, NodeBuilderFlags.NoTruncation, {
2803            trackSymbol: (symbol, declaration, meaning) => {
2804                typeIsAccessible = typeIsAccessible && checker.isSymbolAccessible(symbol, declaration, meaning, /*shouldComputeAliasToMarkVisible*/ false).accessibility === SymbolAccessibility.Accessible;
2805                return !typeIsAccessible;
2806            },
2807            reportInaccessibleThisError: notAccessible,
2808            reportPrivateInBaseOfClassExpression: notAccessible,
2809            reportInaccessibleUniqueSymbolError: notAccessible,
2810            moduleResolverHost: getModuleSpecifierResolverHost(program, host)
2811        });
2812        return typeIsAccessible ? res : undefined;
2813    }
2814
2815    function syntaxRequiresTrailingCommaOrSemicolonOrASI(kind: SyntaxKind) {
2816        return kind === SyntaxKind.CallSignature
2817            || kind === SyntaxKind.ConstructSignature
2818            || kind === SyntaxKind.IndexSignature
2819            || kind === SyntaxKind.PropertySignature
2820            || kind === SyntaxKind.MethodSignature;
2821    }
2822
2823    function syntaxRequiresTrailingFunctionBlockOrSemicolonOrASI(kind: SyntaxKind) {
2824        return kind === SyntaxKind.FunctionDeclaration
2825            || kind === SyntaxKind.Constructor
2826            || kind === SyntaxKind.MethodDeclaration
2827            || kind === SyntaxKind.GetAccessor
2828            || kind === SyntaxKind.SetAccessor;
2829    }
2830
2831    function syntaxRequiresTrailingModuleBlockOrSemicolonOrASI(kind: SyntaxKind) {
2832        return kind === SyntaxKind.ModuleDeclaration;
2833    }
2834
2835    export function syntaxRequiresTrailingSemicolonOrASI(kind: SyntaxKind) {
2836        return kind === SyntaxKind.VariableStatement
2837            || kind === SyntaxKind.ExpressionStatement
2838            || kind === SyntaxKind.DoStatement
2839            || kind === SyntaxKind.ContinueStatement
2840            || kind === SyntaxKind.BreakStatement
2841            || kind === SyntaxKind.ReturnStatement
2842            || kind === SyntaxKind.ThrowStatement
2843            || kind === SyntaxKind.DebuggerStatement
2844            || kind === SyntaxKind.PropertyDeclaration
2845            || kind === SyntaxKind.TypeAliasDeclaration
2846            || kind === SyntaxKind.ImportDeclaration
2847            || kind === SyntaxKind.ImportEqualsDeclaration
2848            || kind === SyntaxKind.ExportDeclaration
2849            || kind === SyntaxKind.NamespaceExportDeclaration
2850            || kind === SyntaxKind.ExportAssignment;
2851    }
2852
2853    export const syntaxMayBeASICandidate = or(
2854        syntaxRequiresTrailingCommaOrSemicolonOrASI,
2855        syntaxRequiresTrailingFunctionBlockOrSemicolonOrASI,
2856        syntaxRequiresTrailingModuleBlockOrSemicolonOrASI,
2857        syntaxRequiresTrailingSemicolonOrASI);
2858
2859    function nodeIsASICandidate(node: Node, sourceFile: SourceFileLike): boolean {
2860        const lastToken = node.getLastToken(sourceFile);
2861        if (lastToken && lastToken.kind === SyntaxKind.SemicolonToken) {
2862            return false;
2863        }
2864
2865        if (syntaxRequiresTrailingCommaOrSemicolonOrASI(node.kind)) {
2866            if (lastToken && lastToken.kind === SyntaxKind.CommaToken) {
2867                return false;
2868            }
2869        }
2870        else if (syntaxRequiresTrailingModuleBlockOrSemicolonOrASI(node.kind)) {
2871            const lastChild = last(node.getChildren(sourceFile));
2872            if (lastChild && isModuleBlock(lastChild)) {
2873                return false;
2874            }
2875        }
2876        else if (syntaxRequiresTrailingFunctionBlockOrSemicolonOrASI(node.kind)) {
2877            const lastChild = last(node.getChildren(sourceFile));
2878            if (lastChild && isFunctionBlock(lastChild)) {
2879                return false;
2880            }
2881        }
2882        else if (!syntaxRequiresTrailingSemicolonOrASI(node.kind)) {
2883            return false;
2884        }
2885
2886        // See comment in parser’s `parseDoStatement`
2887        if (node.kind === SyntaxKind.DoStatement) {
2888            return true;
2889        }
2890
2891        const topNode = findAncestor(node, ancestor => !ancestor.parent)!;
2892        const nextToken = findNextToken(node, topNode, sourceFile);
2893        if (!nextToken || nextToken.kind === SyntaxKind.CloseBraceToken) {
2894            return true;
2895        }
2896
2897        const startLine = sourceFile.getLineAndCharacterOfPosition(node.getEnd()).line;
2898        const endLine = sourceFile.getLineAndCharacterOfPosition(nextToken.getStart(sourceFile)).line;
2899        return startLine !== endLine;
2900    }
2901
2902    export function positionIsASICandidate(pos: number, context: Node, sourceFile: SourceFileLike): boolean {
2903        const contextAncestor = findAncestor(context, ancestor => {
2904            if (ancestor.end !== pos) {
2905                return "quit";
2906            }
2907            return syntaxMayBeASICandidate(ancestor.kind);
2908        });
2909
2910        return !!contextAncestor && nodeIsASICandidate(contextAncestor, sourceFile);
2911    }
2912
2913    export function probablyUsesSemicolons(sourceFile: SourceFile): boolean {
2914        let withSemicolon = 0;
2915        let withoutSemicolon = 0;
2916        const nStatementsToObserve = 5;
2917        forEachChild(sourceFile, function visit(node): boolean | undefined {
2918            if (syntaxRequiresTrailingSemicolonOrASI(node.kind)) {
2919                const lastToken = node.getLastToken(sourceFile);
2920                if (lastToken?.kind === SyntaxKind.SemicolonToken) {
2921                    withSemicolon++;
2922                }
2923                else {
2924                    withoutSemicolon++;
2925                }
2926            }
2927            else if (syntaxRequiresTrailingCommaOrSemicolonOrASI(node.kind)) {
2928                const lastToken = node.getLastToken(sourceFile);
2929                if (lastToken?.kind === SyntaxKind.SemicolonToken) {
2930                    withSemicolon++;
2931                }
2932                else if (lastToken && lastToken.kind !== SyntaxKind.CommaToken) {
2933                    const lastTokenLine = getLineAndCharacterOfPosition(sourceFile, lastToken.getStart(sourceFile)).line;
2934                    const nextTokenLine = getLineAndCharacterOfPosition(sourceFile, getSpanOfTokenAtPosition(sourceFile, lastToken.end).start).line;
2935                    // Avoid counting missing semicolon in single-line objects:
2936                    // `function f(p: { x: string /*no semicolon here is insignificant*/ }) {`
2937                    if (lastTokenLine !== nextTokenLine) {
2938                        withoutSemicolon++;
2939                    }
2940                }
2941            }
2942
2943            if (withSemicolon + withoutSemicolon >= nStatementsToObserve) {
2944                return true;
2945            }
2946
2947            return forEachChild(node, visit);
2948        });
2949
2950        // One statement missing a semicolon isn't sufficient evidence to say the user
2951        // doesn’t want semicolons, because they may not even be done writing that statement.
2952        if (withSemicolon === 0 && withoutSemicolon <= 1) {
2953            return true;
2954        }
2955
2956        // If even 2/5 places have a semicolon, the user probably wants semicolons
2957        return withSemicolon / withoutSemicolon > 1 / nStatementsToObserve;
2958    }
2959
2960    export function tryGetDirectories(host: Pick<LanguageServiceHost, "getDirectories">, directoryName: string): string[] {
2961        return tryIOAndConsumeErrors(host, host.getDirectories, directoryName) || [];
2962    }
2963
2964    export function tryReadDirectory(host: Pick<LanguageServiceHost, "readDirectory">, path: string, extensions?: readonly string[], exclude?: readonly string[], include?: readonly string[]): readonly string[] {
2965        return tryIOAndConsumeErrors(host, host.readDirectory, path, extensions, exclude, include) || emptyArray;
2966    }
2967
2968    export function tryFileExists(host: Pick<LanguageServiceHost, "fileExists">, path: string): boolean {
2969        return tryIOAndConsumeErrors(host, host.fileExists, path);
2970    }
2971
2972    export function tryDirectoryExists(host: LanguageServiceHost, path: string): boolean {
2973        return tryAndIgnoreErrors(() => directoryProbablyExists(path, host)) || false;
2974    }
2975
2976    export function tryAndIgnoreErrors<T>(cb: () => T): T | undefined {
2977        try {
2978            return cb();
2979        }
2980        catch {
2981            return undefined;
2982        }
2983    }
2984
2985    export function tryIOAndConsumeErrors<T>(host: unknown, toApply: ((...a: any[]) => T) | undefined, ...args: any[]) {
2986        return tryAndIgnoreErrors(() => toApply && toApply.apply(host, args));
2987    }
2988
2989    export function findPackageJsons(startDirectory: string, host: LanguageServiceHost, stopDirectory?: string): string[] {
2990        const paths: string[] = [];
2991        forEachAncestorDirectory(startDirectory, ancestor => {
2992            if (ancestor === stopDirectory) {
2993                return true;
2994            }
2995            const currentConfigPath = combinePaths(ancestor, getPackageJsonByPMType(host.getCompilationSettings().packageManagerType));
2996            if (tryFileExists(host, currentConfigPath)) {
2997                paths.push(currentConfigPath);
2998            }
2999        });
3000        return paths;
3001    }
3002
3003    export function findPackageJson(directory: string, host: LanguageServiceHost): string | undefined {
3004        let packageJson: string | undefined;
3005        forEachAncestorDirectory(directory, ancestor => {
3006            const moduleType: string = getModuleByPMType(host.getCompilationSettings().packageManagerType);
3007            const packageJsonType: string = getPackageJsonByPMType(host.getCompilationSettings().packageManagerType);
3008            if (ancestor === moduleType) return true;
3009            packageJson = findConfigFile(ancestor, (f) => tryFileExists(host, f), packageJsonType);
3010            if (packageJson) {
3011                return true; // break out
3012            }
3013        });
3014        return packageJson;
3015    }
3016
3017    export function getPackageJsonsVisibleToFile(fileName: string, host: LanguageServiceHost): readonly ProjectPackageJsonInfo[] {
3018        if (!host.fileExists) {
3019            return [];
3020        }
3021
3022        const packageJsons: ProjectPackageJsonInfo[] = [];
3023        forEachAncestorDirectory(getDirectoryPath(fileName), ancestor => {
3024            const packageJsonFileName = combinePaths(ancestor, getPackageJsonByPMType(host.getCompilationSettings().packageManagerType));
3025            if (host.fileExists(packageJsonFileName)) {
3026                const info = createPackageJsonInfo(packageJsonFileName, host);
3027                if (info) {
3028                    packageJsons.push(info);
3029                }
3030            }
3031        });
3032
3033        return packageJsons;
3034    }
3035
3036    export function createPackageJsonInfo(fileName: string, host: { readFile?(fileName: string): string | undefined }): ProjectPackageJsonInfo | undefined {
3037        if (!host.readFile) {
3038            return undefined;
3039        }
3040
3041        type PackageJsonRaw = Record<typeof dependencyKeys[number], Record<string, string> | undefined>;
3042        const dependencyKeys = ["dependencies", "devDependencies", "optionalDependencies", "peerDependencies"] as const;
3043        const stringContent = host.readFile(fileName) || "";
3044        const content = tryParseJson(stringContent) as PackageJsonRaw | undefined;
3045        const info: Pick<ProjectPackageJsonInfo, typeof dependencyKeys[number]> = {};
3046        if (content) {
3047            for (const key of dependencyKeys) {
3048                const dependencies = content[key];
3049                if (!dependencies) {
3050                    continue;
3051                }
3052                const dependencyMap = new Map<string, string>();
3053                for (const packageName in dependencies) {
3054                    dependencyMap.set(packageName, dependencies[packageName]);
3055                }
3056                info[key] = dependencyMap;
3057            }
3058        }
3059
3060        const dependencyGroups = [
3061            [PackageJsonDependencyGroup.Dependencies, info.dependencies],
3062            [PackageJsonDependencyGroup.DevDependencies, info.devDependencies],
3063            [PackageJsonDependencyGroup.OptionalDependencies, info.optionalDependencies],
3064            [PackageJsonDependencyGroup.PeerDependencies, info.peerDependencies],
3065        ] as const;
3066
3067        return {
3068            ...info,
3069            parseable: !!content,
3070            fileName,
3071            get,
3072            has(dependencyName, inGroups) {
3073                return !!get(dependencyName, inGroups);
3074            },
3075        };
3076
3077        function get(dependencyName: string, inGroups = PackageJsonDependencyGroup.All) {
3078            for (const [group, deps] of dependencyGroups) {
3079                if (deps && (inGroups & group)) {
3080                    const dep = deps.get(dependencyName);
3081                    if (dep !== undefined) {
3082                        return dep;
3083                    }
3084                }
3085            }
3086        }
3087    }
3088
3089    export interface PackageJsonImportFilter {
3090        allowsImportingAmbientModule: (moduleSymbol: Symbol, moduleSpecifierResolutionHost: ModuleSpecifierResolutionHost) => boolean;
3091        allowsImportingSourceFile: (sourceFile: SourceFile, moduleSpecifierResolutionHost: ModuleSpecifierResolutionHost) => boolean;
3092        /**
3093         * Use for a specific module specifier that has already been resolved.
3094         * Use `allowsImportingAmbientModule` or `allowsImportingSourceFile` to resolve
3095         * the best module specifier for a given module _and_ determine if it’s importable.
3096         */
3097        allowsImportingSpecifier: (moduleSpecifier: string) => boolean;
3098    }
3099
3100    export function createPackageJsonImportFilter(fromFile: SourceFile, preferences: UserPreferences, host: LanguageServiceHost): PackageJsonImportFilter {
3101        const packageJsons = (
3102            (host.getPackageJsonsVisibleToFile && host.getPackageJsonsVisibleToFile(fromFile.fileName)) || getPackageJsonsVisibleToFile(fromFile.fileName, host)
3103          ).filter(p => p.parseable);
3104
3105        let usesNodeCoreModules: boolean | undefined;
3106        return { allowsImportingAmbientModule, allowsImportingSourceFile, allowsImportingSpecifier };
3107
3108        function moduleSpecifierIsCoveredByPackageJson(specifier: string) {
3109            const packageName = getNodeModuleRootSpecifier(specifier);
3110            for (const packageJson of packageJsons) {
3111                if (packageJson.has(packageName) || packageJson.has(getTypesPackageName(packageName))) {
3112                    return true;
3113                }
3114            }
3115            return false;
3116        }
3117
3118        function allowsImportingAmbientModule(moduleSymbol: Symbol, moduleSpecifierResolutionHost: ModuleSpecifierResolutionHost): boolean {
3119            if (!packageJsons.length || !moduleSymbol.valueDeclaration) {
3120                return true;
3121            }
3122
3123            const declaringSourceFile = moduleSymbol.valueDeclaration.getSourceFile();
3124            const declaringNodeModuleName = getNodeModulesPackageNameFromFileName(declaringSourceFile.fileName, moduleSpecifierResolutionHost);
3125            if (typeof declaringNodeModuleName === "undefined") {
3126                return true;
3127            }
3128
3129            const declaredModuleSpecifier = stripQuotes(moduleSymbol.getName());
3130            if (isAllowedCoreNodeModulesImport(declaredModuleSpecifier)) {
3131                return true;
3132            }
3133
3134            return moduleSpecifierIsCoveredByPackageJson(declaringNodeModuleName)
3135                || moduleSpecifierIsCoveredByPackageJson(declaredModuleSpecifier);
3136        }
3137
3138        function allowsImportingSourceFile(sourceFile: SourceFile, moduleSpecifierResolutionHost: ModuleSpecifierResolutionHost): boolean {
3139            if (!packageJsons.length) {
3140                return true;
3141            }
3142
3143            const moduleSpecifier = getNodeModulesPackageNameFromFileName(sourceFile.fileName, moduleSpecifierResolutionHost);
3144            if (!moduleSpecifier) {
3145                return true;
3146            }
3147
3148            return moduleSpecifierIsCoveredByPackageJson(moduleSpecifier);
3149        }
3150
3151        function allowsImportingSpecifier(moduleSpecifier: string) {
3152            if (!packageJsons.length || isAllowedCoreNodeModulesImport(moduleSpecifier)) {
3153                return true;
3154            }
3155            if (pathIsRelative(moduleSpecifier) || isRootedDiskPath(moduleSpecifier)) {
3156                return true;
3157            }
3158            return moduleSpecifierIsCoveredByPackageJson(moduleSpecifier);
3159        }
3160
3161        function isAllowedCoreNodeModulesImport(moduleSpecifier: string) {
3162            // If we’re in JavaScript, it can be difficult to tell whether the user wants to import
3163            // from Node core modules or not. We can start by seeing if the user is actually using
3164            // any node core modules, as opposed to simply having @types/node accidentally as a
3165            // dependency of a dependency.
3166            if (isSourceFileJS(fromFile) && JsTyping.nodeCoreModules.has(moduleSpecifier)) {
3167                if (usesNodeCoreModules === undefined) {
3168                    usesNodeCoreModules = consumesNodeCoreModules(fromFile);
3169                }
3170                if (usesNodeCoreModules) {
3171                    return true;
3172                }
3173            }
3174            return false;
3175        }
3176
3177        function getNodeModulesPackageNameFromFileName(importedFileName: string, moduleSpecifierResolutionHost: ModuleSpecifierResolutionHost): string | undefined {
3178            if (!stringContains(importedFileName, "node_modules") && !stringContains(importedFileName, "oh_modules")) {
3179                return undefined;
3180            }
3181            const specifier = moduleSpecifiers.getNodeModulesPackageName(
3182                host.getCompilationSettings(),
3183                fromFile,
3184                importedFileName,
3185                moduleSpecifierResolutionHost,
3186                preferences,
3187            );
3188
3189            if (!specifier) {
3190                return undefined;
3191            }
3192            // Paths here are not node_modules, so we don’t care about them;
3193            // returning anything will trigger a lookup in package.json.
3194            if (!pathIsRelative(specifier) && !isRootedDiskPath(specifier)) {
3195                return getNodeModuleRootSpecifier(specifier);
3196            }
3197        }
3198
3199        function getNodeModuleRootSpecifier(fullSpecifier: string): string {
3200            const components = getPathComponents(getPackageNameFromTypesPackageName(fullSpecifier)).slice(1);
3201            // Scoped packages
3202            if (startsWith(components[0], "@")) {
3203                return `${components[0]}/${components[1]}`;
3204            }
3205            return components[0];
3206        }
3207    }
3208
3209    function tryParseJson(text: string) {
3210        try {
3211            return JSON.parse(text);
3212        }
3213        catch {
3214            return undefined;
3215        }
3216    }
3217
3218    export function consumesNodeCoreModules(sourceFile: SourceFile): boolean {
3219        return some(sourceFile.imports, ({ text }) => JsTyping.nodeCoreModules.has(text));
3220    }
3221
3222    export function isInsideNodeModules(fileOrDirectory: string): boolean {
3223        return contains(getPathComponents(fileOrDirectory), "node_modules");
3224    }
3225
3226    export function isDiagnosticWithLocation(diagnostic: Diagnostic): diagnostic is DiagnosticWithLocation {
3227        return diagnostic.file !== undefined && diagnostic.start !== undefined && diagnostic.length !== undefined;
3228    }
3229
3230    export function findDiagnosticForNode(node: Node, sortedFileDiagnostics: readonly Diagnostic[]): DiagnosticWithLocation | undefined {
3231        const span: Partial<TextSpan> = createTextSpanFromNode(node);
3232        const index = binarySearchKey(sortedFileDiagnostics, span, identity, compareTextSpans);
3233        if (index >= 0) {
3234            const diagnostic = sortedFileDiagnostics[index];
3235            Debug.assertEqual(diagnostic.file, node.getSourceFile(), "Diagnostics proided to 'findDiagnosticForNode' must be from a single SourceFile");
3236            return cast(diagnostic, isDiagnosticWithLocation);
3237        }
3238    }
3239
3240    export function getDiagnosticsWithinSpan(span: TextSpan, sortedFileDiagnostics: readonly Diagnostic[]): readonly DiagnosticWithLocation[] {
3241        let index = binarySearchKey(sortedFileDiagnostics, span.start, diag => diag.start, compareValues);
3242        if (index < 0) {
3243            index = ~index;
3244        }
3245        while (sortedFileDiagnostics[index - 1]?.start === span.start) {
3246            index--;
3247        }
3248
3249        const result: DiagnosticWithLocation[] = [];
3250        const end = textSpanEnd(span);
3251        while (true) {
3252            const diagnostic = tryCast(sortedFileDiagnostics[index], isDiagnosticWithLocation);
3253            if (!diagnostic || diagnostic.start > end) {
3254                break;
3255            }
3256            if (textSpanContainsTextSpan(span, diagnostic)) {
3257                result.push(diagnostic);
3258            }
3259            index++;
3260        }
3261
3262        return result;
3263    }
3264
3265    /* @internal */
3266    export function getRefactorContextSpan({ startPosition, endPosition }: RefactorContext): TextSpan {
3267        return createTextSpanFromBounds(startPosition, endPosition === undefined ? startPosition : endPosition);
3268    }
3269
3270    /* @internal */
3271    export function getFixableErrorSpanExpression(sourceFile: SourceFile, span: TextSpan): Expression | undefined {
3272        const token = getTokenAtPosition(sourceFile, span.start);
3273        // Checker has already done work to determine that await might be possible, and has attached
3274        // related info to the node, so start by finding the expression that exactly matches up
3275        // with the diagnostic range.
3276        const expression = findAncestor(token, node => {
3277            if (node.getStart(sourceFile) < span.start || node.getEnd() > textSpanEnd(span)) {
3278                return "quit";
3279            }
3280            return isExpression(node) && textSpansEqual(span, createTextSpanFromNode(node, sourceFile));
3281        }) as Expression | undefined;
3282
3283        return expression;
3284    }
3285
3286    /**
3287     * If the provided value is an array, the mapping function is applied to each element; otherwise, the mapping function is applied
3288     * to the provided value itself.
3289     */
3290    export function mapOneOrMany<T, U>(valueOrArray: T | readonly T[], f: (x: T, i: number) => U): U | U[];
3291    export function mapOneOrMany<T, U>(valueOrArray: T | readonly T[] | undefined, f: (x: T, i: number) => U): U | U[] | undefined;
3292    export function mapOneOrMany<T, U>(valueOrArray: T | readonly T[], f: (x: T, i: number) => U, resultSelector: (x: U[]) => U): U;
3293    export function mapOneOrMany<T, U>(valueOrArray: T | readonly T[] | undefined, f: (x: T, i: number) => U, resultSelector: (x: U[]) => U): U | undefined;
3294    export function mapOneOrMany<T, U>(valueOrArray: T | readonly T[] | undefined, f: (x: T, i: number) => U, resultSelector: (x: U[]) => U | U[] = identity): U | U[] | undefined {
3295        return valueOrArray ? isArray(valueOrArray) ? resultSelector(map(valueOrArray, f)) : f(valueOrArray, 0) : undefined;
3296    }
3297
3298    /**
3299     * If the provided value is an array, the first element of the array is returned; otherwise, the provided value is returned instead.
3300     */
3301    export function firstOrOnly<T>(valueOrArray: T | readonly T[]): T {
3302        return isArray(valueOrArray) ? first(valueOrArray) : valueOrArray;
3303    }
3304
3305    export function getNamesForExportedSymbol(symbol: Symbol, scriptTarget: ScriptTarget | undefined): string | [lowercase: string, capitalized: string] {
3306        if (needsNameFromDeclaration(symbol)) {
3307            const fromDeclaration = getDefaultLikeExportNameFromDeclaration(symbol);
3308            if (fromDeclaration) return fromDeclaration;
3309            const fileNameCase = codefix.moduleSymbolToValidIdentifier(getSymbolParentOrFail(symbol), scriptTarget, /*preferCapitalized*/ false);
3310            const capitalized = codefix.moduleSymbolToValidIdentifier(getSymbolParentOrFail(symbol), scriptTarget, /*preferCapitalized*/ true);
3311            if (fileNameCase === capitalized) return fileNameCase;
3312            return [fileNameCase, capitalized];
3313        }
3314        return symbol.name;
3315    }
3316
3317    export function getNameForExportedSymbol(symbol: Symbol, scriptTarget: ScriptTarget | undefined, preferCapitalized?: boolean) {
3318        if (needsNameFromDeclaration(symbol)) {
3319            // Name of "export default foo;" is "foo". Name of "export default 0" is the filename converted to camelCase.
3320            return getDefaultLikeExportNameFromDeclaration(symbol)
3321                || codefix.moduleSymbolToValidIdentifier(getSymbolParentOrFail(symbol), scriptTarget, !!preferCapitalized);
3322        }
3323        return symbol.name;
3324
3325    }
3326
3327    function needsNameFromDeclaration(symbol: Symbol) {
3328        return !(symbol.flags & SymbolFlags.Transient) && (symbol.escapedName === InternalSymbolName.ExportEquals || symbol.escapedName === InternalSymbolName.Default);
3329    }
3330
3331    function getDefaultLikeExportNameFromDeclaration(symbol: Symbol) {
3332        return firstDefined(symbol.declarations, d => isExportAssignment(d) ? tryCast(skipOuterExpressions(d.expression), isIdentifier)?.text : undefined);
3333    }
3334
3335    function getSymbolParentOrFail(symbol: Symbol) {
3336        return Debug.checkDefined(
3337            symbol.parent,
3338            `Symbol parent was undefined. Flags: ${Debug.formatSymbolFlags(symbol.flags)}. ` +
3339            `Declarations: ${symbol.declarations?.map(d => {
3340                const kind = Debug.formatSyntaxKind(d.kind);
3341                const inJS = isInJSFile(d);
3342                const { expression } = d as any;
3343                return (inJS ? "[JS]" : "") + kind + (expression ? ` (expression: ${Debug.formatSyntaxKind(expression.kind)})` : "");
3344            }).join(", ")}.`);
3345    }
3346
3347    /**
3348     * Useful to check whether a string contains another string at a specific index
3349     * without allocating another string or traversing the entire contents of the outer string.
3350     *
3351     * This function is useful in place of either of the following:
3352     *
3353     * ```ts
3354     * // Allocates
3355     * haystack.substr(startIndex, needle.length) === needle
3356     *
3357     * // Full traversal
3358     * haystack.indexOf(needle, startIndex) === startIndex
3359     * ```
3360     *
3361     * @param haystack The string that potentially contains `needle`.
3362     * @param needle The string whose content might sit within `haystack`.
3363     * @param startIndex The index within `haystack` to start searching for `needle`.
3364     */
3365    export function stringContainsAt(haystack: string, needle: string, startIndex: number) {
3366        const needleLength = needle.length;
3367        if (needleLength + startIndex > haystack.length) {
3368            return false;
3369        }
3370        for (let i = 0; i < needleLength; i++) {
3371            if (needle.charCodeAt(i) !== haystack.charCodeAt(i + startIndex)) return false;
3372        }
3373        return true;
3374    }
3375
3376    export function startsWithUnderscore(name: string): boolean {
3377        return name.charCodeAt(0) === CharacterCodes._;
3378    }
3379
3380    export function isGlobalDeclaration(declaration: Declaration) {
3381        return !isNonGlobalDeclaration(declaration);
3382    }
3383
3384    export function isNonGlobalDeclaration(declaration: Declaration) {
3385        const sourceFile = declaration.getSourceFile();
3386        // If the file is not a module, the declaration is global
3387        if (!sourceFile.externalModuleIndicator && !sourceFile.commonJsModuleIndicator) {
3388            return false;
3389        }
3390        // If the file is a module written in TypeScript, it still might be in a `declare global` augmentation
3391        return isInJSFile(declaration) || !findAncestor(declaration, isGlobalScopeAugmentation);
3392    }
3393
3394    export function isDeprecatedDeclaration(decl: Declaration) {
3395        return !!(getCombinedNodeFlagsAlwaysIncludeJSDoc(decl) & ModifierFlags.Deprecated);
3396    }
3397
3398    export function shouldUseUriStyleNodeCoreModules(file: SourceFile, program: Program): boolean {
3399        const decisionFromFile = firstDefined(file.imports, node => {
3400            if (JsTyping.nodeCoreModules.has(node.text)) {
3401                return startsWith(node.text, "node:");
3402            }
3403        });
3404        return decisionFromFile ?? program.usesUriStyleNodeCoreModules;
3405    }
3406
3407    export function getNewLineKind(newLineCharacter: string): NewLineKind {
3408        return newLineCharacter === "\n" ? NewLineKind.LineFeed : NewLineKind.CarriageReturnLineFeed;
3409    }
3410
3411    export type DiagnosticAndArguments = DiagnosticMessage | [DiagnosticMessage, string] | [DiagnosticMessage, string, string];
3412    export function diagnosticToString(diag: DiagnosticAndArguments): string {
3413        return isArray(diag)
3414            ? formatStringFromArgs(getLocaleSpecificMessage(diag[0]), diag.slice(1) as readonly string[])
3415            : getLocaleSpecificMessage(diag);
3416    }
3417
3418    /**
3419     * Get format code settings for a code writing context (e.g. when formatting text changes or completions code).
3420     */
3421    export function getFormatCodeSettingsForWriting({ options }: formatting.FormatContext, sourceFile: SourceFile): FormatCodeSettings {
3422        const shouldAutoDetectSemicolonPreference = !options.semicolons || options.semicolons === SemicolonPreference.Ignore;
3423        const shouldRemoveSemicolons = options.semicolons === SemicolonPreference.Remove || shouldAutoDetectSemicolonPreference && !probablyUsesSemicolons(sourceFile);
3424        return {
3425            ...options,
3426            semicolons: shouldRemoveSemicolons ? SemicolonPreference.Remove : SemicolonPreference.Ignore,
3427        };
3428    }
3429
3430    export function jsxModeNeedsExplicitImport(jsx: JsxEmit | undefined) {
3431        return jsx === JsxEmit.React || jsx === JsxEmit.ReactNative;
3432    }
3433
3434    export function isSourceFileFromLibrary(program: Program, node: SourceFile) {
3435        return program.isSourceFileFromExternalLibrary(node) || program.isSourceFileDefaultLibrary(node);
3436    }
3437
3438    export function isVirtualConstructor(checker: TypeChecker, symbol: Symbol, node: Node): boolean {
3439        const symbolName = checker.symbolToString(symbol); // Do not get scoped name, just the name of the symbol
3440        const symbolKind = SymbolDisplay.getSymbolKind(checker, symbol, node);
3441        if (node.virtual && symbolName === InternalSymbolName.Constructor && symbolKind === ScriptElementKind.constructorImplementationElement) {
3442            return true;
3443        }
3444        return false;
3445    }
3446
3447    export function getDeclarationFromSymbol(symbol: Symbol | undefined, position = "first"): Declaration | undefined {
3448        return symbol?.valueDeclaration ?? (symbol?.declarations && symbol.declarations.length ? position === "first" ? first(symbol.declarations) : last(symbol.declarations) : undefined);
3449    }
3450
3451    export function isVirtualAttributeTypeArgument(node: CallExpression | EtsComponentExpression): boolean {
3452        return node.typeArguments ?
3453            some(node.typeArguments,
3454                (argument) => isTypeReferenceNode(argument) &&
3455                !!argument.virtual &&
3456                isIdentifier(argument.typeName) &&
3457                !!argument.typeName.escapedText.toString().match("Attribute")
3458            ) : true;
3459    }
3460
3461    export function getTextOfJSDocTagInfo(tag: JSDocTagInfo): string {
3462        if (!tag.text) {
3463            return "";
3464        }
3465        if (Array.isArray(tag.text)) {
3466            return displayPartsToString(tag.text);
3467        } else {
3468            return getTextOfJSDocComment(tag.text) ?? "";
3469        }
3470    }
3471
3472    // #endregion
3473}
3474