1namespace ts {
2    /** The version of the language service API */
3    export const servicesVersion = "0.8";
4
5    function createNode<TKind extends SyntaxKind>(kind: TKind, pos: number, end: number, parent: Node): NodeObject | TokenObject<TKind> | IdentifierObject | PrivateIdentifierObject {
6        const node = isNodeKind(kind) ? new NodeObject(kind, pos, end) :
7            kind === SyntaxKind.Identifier ? new IdentifierObject(SyntaxKind.Identifier, pos, end) :
8                kind === SyntaxKind.PrivateIdentifier ? new PrivateIdentifierObject(SyntaxKind.PrivateIdentifier, pos, end) :
9                    new TokenObject(kind, pos, end);
10        node.parent = parent;
11        node.flags = parent.flags & NodeFlags.ContextFlags;
12        return node;
13    }
14
15    class NodeObject implements Node {
16        public kind: SyntaxKind;
17        public pos: number;
18        public end: number;
19        public flags: NodeFlags;
20        public modifierFlagsCache: ModifierFlags;
21        public transformFlags: TransformFlags;
22        public parent: Node;
23        public symbol!: Symbol; // Actually optional, but it was too annoying to access `node.symbol!` everywhere since in many cases we know it must be defined
24        public jsDoc?: JSDoc[];
25        public original?: Node;
26        private _children: Node[] | undefined;
27
28        constructor(kind: SyntaxKind, pos: number, end: number) {
29            this.pos = pos;
30            this.end = end;
31            this.flags = NodeFlags.None;
32            this.modifierFlagsCache = ModifierFlags.None;
33            this.transformFlags = TransformFlags.None;
34            this.parent = undefined!;
35            this.kind = kind;
36        }
37
38        private assertHasRealPosition(message?: string) {
39            // eslint-disable-next-line local/debug-assert
40            Debug.assert(!positionIsSynthesized(this.pos) && !positionIsSynthesized(this.end), message || "Node must have a real position for this operation");
41        }
42
43        public getSourceFile(): SourceFile {
44            return getSourceFileOfNode(this);
45        }
46
47        public getStart(sourceFile?: SourceFileLike, includeJsDocComment?: boolean): number {
48            this.assertHasRealPosition();
49            return getTokenPosOfNode(this, sourceFile, includeJsDocComment);
50        }
51
52        public getFullStart(): number {
53            this.assertHasRealPosition();
54            return this.pos;
55        }
56
57        public getEnd(): number {
58            this.assertHasRealPosition();
59            return this.end;
60        }
61
62        public getWidth(sourceFile?: SourceFile): number {
63            this.assertHasRealPosition();
64            return this.getEnd() - this.getStart(sourceFile);
65        }
66
67        public getFullWidth(): number {
68            this.assertHasRealPosition();
69            return this.end - this.pos;
70        }
71
72        public getLeadingTriviaWidth(sourceFile?: SourceFile): number {
73            this.assertHasRealPosition();
74            return this.getStart(sourceFile) - this.pos;
75        }
76
77        public getFullText(sourceFile?: SourceFile): string {
78            this.assertHasRealPosition();
79            return (sourceFile || this.getSourceFile()).text.substring(this.pos, this.end);
80        }
81
82        public getText(sourceFile?: SourceFile): string {
83            this.assertHasRealPosition();
84            if (!sourceFile) {
85                sourceFile = this.getSourceFile();
86            }
87            return sourceFile.text.substring(this.getStart(sourceFile), this.getEnd());
88        }
89
90        public getChildCount(sourceFile?: SourceFile): number {
91            return this.getChildren(sourceFile).length;
92        }
93
94        public getChildAt(index: number, sourceFile?: SourceFile): Node {
95            return this.getChildren(sourceFile)[index];
96        }
97
98        public getChildren(sourceFile?: SourceFileLike): Node[] {
99            this.assertHasRealPosition("Node without a real position cannot be scanned and thus has no token nodes - use forEachChild and collect the result if that's fine");
100            return this._children || (this._children = createChildren(this, sourceFile));
101        }
102
103        public getFirstToken(sourceFile?: SourceFileLike): Node | undefined {
104            this.assertHasRealPosition();
105            const children = this.getChildren(sourceFile);
106            if (!children.length) {
107                return undefined;
108            }
109
110            const child = find(children, kid => kid.kind < SyntaxKind.FirstJSDocNode || kid.kind > SyntaxKind.LastJSDocNode)!;
111            return child.kind < SyntaxKind.FirstNode ?
112                child :
113                child.getFirstToken(sourceFile);
114        }
115
116        public getLastToken(sourceFile?: SourceFileLike): Node | undefined {
117            this.assertHasRealPosition();
118            const children = this.getChildren(sourceFile);
119
120            const child = lastOrUndefined(children);
121            if (!child) {
122                return undefined;
123            }
124
125            return child.kind < SyntaxKind.FirstNode ? child : child.getLastToken(sourceFile);
126        }
127
128        public forEachChild<T>(cbNode: (node: Node) => T, cbNodeArray?: (nodes: NodeArray<Node>) => T): T | undefined {
129            return forEachChild(this, cbNode, cbNodeArray);
130        }
131    }
132
133    function createChildren(node: Node, sourceFile: SourceFileLike | undefined): Node[] {
134        if (!isNodeKind(node.kind)) {
135            return emptyArray;
136        }
137
138        const children: Node[] = [];
139
140        if (isJSDocCommentContainingNode(node)) {
141            /** Don't add trivia for "tokens" since this is in a comment. */
142            node.forEachChild(child => {
143                children.push(child);
144            });
145            return children;
146        }
147
148        const sourceFileName: string | undefined = sourceFile ? sourceFile.fileName : node.getSourceFile().fileName;
149        if (sourceFileName && getScriptKindFromFileName(sourceFileName) === ScriptKind.ETS) {
150            scanner.setEtsContext(true);
151        }
152
153        scanner.setText((sourceFile || node.getSourceFile()).text);
154        let pos = node.pos;
155        const processNode = (child: Node) => {
156            addSyntheticNodes(children, pos, child.pos, node);
157            children.push(child);
158            pos = child.end;
159        };
160        const processNodes = (nodes: NodeArray<Node>) => {
161            addSyntheticNodes(children, pos, nodes.pos, node);
162            children.push(createSyntaxList(nodes, node));
163            pos = nodes.end;
164        };
165        // jsDocComments need to be the first children
166        forEach((node as JSDocContainer).jsDoc, processNode);
167        // For syntactic classifications, all trivia are classified together, including jsdoc comments.
168        // For that to work, the jsdoc comments should still be the leading trivia of the first child.
169        // Restoring the scanner position ensures that.
170        pos = node.pos;
171        node.forEachChild(processNode, processNodes);
172        addSyntheticNodes(children, pos, node.end, node);
173        scanner.setText(undefined);
174        scanner.setEtsContext(false);
175        return children;
176    }
177
178    function addSyntheticNodes(nodes: Push<Node>, pos: number, end: number, parent: Node): void {
179        // position start === end mean the node is virtual
180        if(parent.virtual){
181            return;
182        }
183        scanner.setTextPos(pos);
184        while (pos < end) {
185            const token = scanner.scan();
186            const textPos = scanner.getTextPos();
187            if (!isSourceFile(parent) || !isInMarkedKitImport(parent, pos, textPos)) {
188                if (textPos <= end) {
189                    if (token === SyntaxKind.Identifier) {
190                        Debug.fail(`Did not expect ${Debug.formatSyntaxKind(parent.kind)} to have an Identifier in its trivia`);
191                    }
192                    nodes.push(createNode(token, pos, textPos, parent));
193                }
194            }
195            pos = textPos;
196            if (token === SyntaxKind.EndOfFileToken) {
197                break;
198            }
199        }
200    }
201
202    function createSyntaxList(nodes: NodeArray<Node>, parent: Node): Node {
203        const list = createNode(SyntaxKind.SyntaxList, nodes.pos, nodes.end, parent) as any as SyntaxList;
204        list._children = [];
205        let pos = nodes.pos;
206        for (const node of nodes) {
207            // position start === end mean the node is visual
208            if (node.virtual) {
209                continue;
210            }
211            addSyntheticNodes(list._children, pos, node.pos, parent);
212            list._children.push(node);
213            pos = node.end;
214        }
215        addSyntheticNodes(list._children, pos, nodes.end, parent);
216        return list;
217    }
218
219    class TokenOrIdentifierObject implements Node {
220        public kind!: SyntaxKind;
221        public pos: number;
222        public end: number;
223        public flags: NodeFlags;
224        public modifierFlagsCache: ModifierFlags;
225        public transformFlags: TransformFlags;
226        public parent: Node;
227        public symbol!: Symbol;
228        public jsDocComments?: JSDoc[];
229
230        constructor(pos: number, end: number) {
231            // Set properties in same order as NodeObject
232            this.pos = pos;
233            this.end = end;
234            this.flags = NodeFlags.None;
235            this.modifierFlagsCache = ModifierFlags.None;
236            this.transformFlags = TransformFlags.None;
237            this.parent = undefined!;
238        }
239
240        public getSourceFile(): SourceFile {
241            return getSourceFileOfNode(this);
242        }
243
244        public getStart(sourceFile?: SourceFileLike, includeJsDocComment?: boolean): number {
245            return getTokenPosOfNode(this, sourceFile, includeJsDocComment);
246        }
247
248        public getFullStart(): number {
249            return this.pos;
250        }
251
252        public getEnd(): number {
253            return this.end;
254        }
255
256        public getWidth(sourceFile?: SourceFile): number {
257            return this.getEnd() - this.getStart(sourceFile);
258        }
259
260        public getFullWidth(): number {
261            return this.end - this.pos;
262        }
263
264        public getLeadingTriviaWidth(sourceFile?: SourceFile): number {
265            return this.getStart(sourceFile) - this.pos;
266        }
267
268        public getFullText(sourceFile?: SourceFile): string {
269            return (sourceFile || this.getSourceFile()).text.substring(this.pos, this.end);
270        }
271
272        public getText(sourceFile?: SourceFile): string {
273            if (!sourceFile) {
274                sourceFile = this.getSourceFile();
275            }
276            return sourceFile.text.substring(this.getStart(sourceFile), this.getEnd());
277        }
278
279        public getChildCount(): number {
280            return this.getChildren().length;
281        }
282
283        public getChildAt(index: number): Node {
284            return this.getChildren()[index];
285        }
286
287        public getChildren(): Node[] {
288            return this.kind === SyntaxKind.EndOfFileToken ? (this as EndOfFileToken).jsDoc || emptyArray : emptyArray;
289        }
290
291        public getFirstToken(): Node | undefined {
292            return undefined;
293        }
294
295        public getLastToken(): Node | undefined {
296            return undefined;
297        }
298
299        public forEachChild<T>(): T | undefined {
300            return undefined;
301        }
302    }
303
304    class SymbolObject implements Symbol {
305        flags: SymbolFlags;
306        escapedName: __String;
307        declarations!: Declaration[];
308        valueDeclaration!: Declaration;
309
310        // Undefined is used to indicate the value has not been computed. If, after computing, the
311        // symbol has no doc comment, then the empty array will be returned.
312        documentationComment?: SymbolDisplayPart[];
313        tags?: JSDocTagInfo[]; // same
314
315        contextualGetAccessorDocumentationComment?: SymbolDisplayPart[];
316        contextualSetAccessorDocumentationComment?: SymbolDisplayPart[];
317
318        contextualGetAccessorTags?: JSDocTagInfo[];
319        contextualSetAccessorTags?: JSDocTagInfo[];
320
321        constructor(flags: SymbolFlags, name: __String) {
322            this.flags = flags;
323            this.escapedName = name;
324        }
325
326        getFlags(): SymbolFlags {
327            return this.flags;
328        }
329
330        get name(): string {
331            return symbolName(this);
332        }
333
334        getEscapedName(): __String {
335            return this.escapedName;
336        }
337
338        getName(): string {
339            return this.name;
340        }
341
342        getDeclarations(): Declaration[] | undefined {
343            return this.declarations;
344        }
345
346        getDocumentationComment(checker: TypeChecker | undefined): SymbolDisplayPart[] {
347            if (!this.documentationComment) {
348                this.documentationComment = emptyArray; // Set temporarily to avoid an infinite loop finding inherited docs
349
350                if (!this.declarations && (this as Symbol as TransientSymbol).target && ((this as Symbol as TransientSymbol).target as TransientSymbol).tupleLabelDeclaration) {
351                    const labelDecl = ((this as Symbol as TransientSymbol).target as TransientSymbol).tupleLabelDeclaration!;
352                    this.documentationComment = getDocumentationComment([labelDecl], checker);
353                }
354                else {
355                    this.documentationComment = getDocumentationComment(this.declarations, checker);
356                }
357            }
358            return this.documentationComment;
359        }
360
361        getContextualDocumentationComment(context: Node | undefined, checker: TypeChecker | undefined): SymbolDisplayPart[] {
362            if (context) {
363                if (isGetAccessor(context)) {
364                    if (!this.contextualGetAccessorDocumentationComment) {
365                        this.contextualGetAccessorDocumentationComment = getDocumentationComment(filter(this.declarations, isGetAccessor), checker);
366                    }
367                    if (length(this.contextualGetAccessorDocumentationComment)) {
368                        return this.contextualGetAccessorDocumentationComment;
369                    }
370                }
371                if (isSetAccessor(context)) {
372                    if (!this.contextualSetAccessorDocumentationComment) {
373                        this.contextualSetAccessorDocumentationComment = getDocumentationComment(filter(this.declarations, isSetAccessor), checker);
374                    }
375                    if (length(this.contextualSetAccessorDocumentationComment)) {
376                        return this.contextualSetAccessorDocumentationComment;
377                    }
378                }
379            }
380            return this.getDocumentationComment(checker);
381        }
382
383        getJsDocTags(checker?: TypeChecker): JSDocTagInfo[] {
384            if (this.tags === undefined) {
385                this.tags = getJsDocTagsOfDeclarations(this.declarations, checker);
386            }
387
388            return this.tags;
389        }
390
391        getContextualJsDocTags(context: Node | undefined, checker: TypeChecker | undefined): JSDocTagInfo[] {
392            if (context) {
393                if (isGetAccessor(context)) {
394                    if (!this.contextualGetAccessorTags) {
395                        this.contextualGetAccessorTags = getJsDocTagsOfDeclarations(filter(this.declarations, isGetAccessor), checker);
396                    }
397                    if (length(this.contextualGetAccessorTags)) {
398                        return this.contextualGetAccessorTags;
399                    }
400                }
401                if (isSetAccessor(context)) {
402                    if (!this.contextualSetAccessorTags) {
403                        this.contextualSetAccessorTags = getJsDocTagsOfDeclarations(filter(this.declarations, isSetAccessor), checker);
404                    }
405                    if (length(this.contextualSetAccessorTags)) {
406                        return this.contextualSetAccessorTags;
407                    }
408                }
409            }
410            return this.getJsDocTags(checker);
411        }
412    }
413
414    class TokenObject<TKind extends SyntaxKind> extends TokenOrIdentifierObject implements Token<TKind> {
415        public kind: TKind;
416
417        constructor(kind: TKind, pos: number, end: number) {
418            super(pos, end);
419            this.kind = kind;
420        }
421    }
422
423    class IdentifierObject extends TokenOrIdentifierObject implements Identifier {
424        public kind: SyntaxKind.Identifier = SyntaxKind.Identifier;
425        public escapedText!: __String;
426        public autoGenerateFlags!: GeneratedIdentifierFlags;
427        _primaryExpressionBrand: any;
428        _memberExpressionBrand: any;
429        _leftHandSideExpressionBrand: any;
430        _updateExpressionBrand: any;
431        _unaryExpressionBrand: any;
432        _expressionBrand: any;
433        _declarationBrand: any;
434        /*@internal*/typeArguments!: NodeArray<TypeNode>;
435        constructor(_kind: SyntaxKind.Identifier, pos: number, end: number) {
436            super(pos, end);
437        }
438
439        get text(): string {
440            return idText(this);
441        }
442    }
443    IdentifierObject.prototype.kind = SyntaxKind.Identifier;
444    class PrivateIdentifierObject extends TokenOrIdentifierObject implements PrivateIdentifier {
445        public kind!: SyntaxKind.PrivateIdentifier;
446        public escapedText!: __String;
447        public symbol!: Symbol;
448        _primaryExpressionBrand: any;
449        _memberExpressionBrand: any;
450        _leftHandSideExpressionBrand: any;
451        _updateExpressionBrand: any;
452        _unaryExpressionBrand: any;
453        _expressionBrand: any;
454        constructor(_kind: SyntaxKind.PrivateIdentifier, pos: number, end: number) {
455            super(pos, end);
456        }
457
458        get text(): string {
459            return idText(this);
460        }
461    }
462    PrivateIdentifierObject.prototype.kind = SyntaxKind.PrivateIdentifier;
463
464    class TypeObject implements Type {
465        checker: TypeChecker;
466        flags: TypeFlags;
467        objectFlags?: ObjectFlags;
468        id!: number;
469        symbol!: Symbol;
470        constructor(checker: TypeChecker, flags: TypeFlags) {
471            this.checker = checker;
472            this.flags = flags;
473        }
474        getFlags(): TypeFlags {
475            return this.flags;
476        }
477        getSymbol(): Symbol | undefined {
478            return this.symbol;
479        }
480        getProperties(): Symbol[] {
481            return this.checker.getPropertiesOfType(this);
482        }
483        getProperty(propertyName: string): Symbol | undefined {
484            return this.checker.getPropertyOfType(this, propertyName);
485        }
486        getApparentProperties(): Symbol[] {
487            return this.checker.getAugmentedPropertiesOfType(this);
488        }
489        getCallSignatures(): readonly Signature[] {
490            return this.checker.getSignaturesOfType(this, SignatureKind.Call);
491        }
492        getConstructSignatures(): readonly Signature[] {
493            return this.checker.getSignaturesOfType(this, SignatureKind.Construct);
494        }
495        getStringIndexType(): Type | undefined {
496            return this.checker.getIndexTypeOfType(this, IndexKind.String);
497        }
498        getNumberIndexType(): Type | undefined {
499            return this.checker.getIndexTypeOfType(this, IndexKind.Number);
500        }
501        getBaseTypes(): BaseType[] | undefined {
502            return this.isClassOrInterface() ? this.checker.getBaseTypes(this) : undefined;
503        }
504        isNullableType(): boolean {
505            return this.checker.isNullableType(this);
506        }
507        getNonNullableType(): Type {
508            return this.checker.getNonNullableType(this);
509        }
510        getNonOptionalType(): Type {
511            return this.checker.getNonOptionalType(this);
512        }
513        getConstraint(): Type | undefined {
514            return this.checker.getBaseConstraintOfType(this);
515        }
516        getDefault(): Type | undefined {
517            return this.checker.getDefaultFromTypeParameter(this);
518        }
519
520        isUnion(): this is UnionType {
521            return !!(this.flags & TypeFlags.Union);
522        }
523        isIntersection(): this is IntersectionType {
524            return !!(this.flags & TypeFlags.Intersection);
525        }
526        isUnionOrIntersection(): this is UnionOrIntersectionType {
527            return !!(this.flags & TypeFlags.UnionOrIntersection);
528        }
529        isLiteral(): this is LiteralType {
530            return !!(this.flags & TypeFlags.StringOrNumberLiteral);
531        }
532        isStringLiteral(): this is StringLiteralType {
533            return !!(this.flags & TypeFlags.StringLiteral);
534        }
535        isNumberLiteral(): this is NumberLiteralType {
536            return !!(this.flags & TypeFlags.NumberLiteral);
537        }
538        isTypeParameter(): this is TypeParameter {
539            return !!(this.flags & TypeFlags.TypeParameter);
540        }
541        isClassOrInterface(): this is InterfaceType {
542            return !!(getObjectFlags(this) & ObjectFlags.ClassOrInterface);
543        }
544        isClass(): this is InterfaceType {
545            return !!(getObjectFlags(this) & ObjectFlags.Class);
546        }
547        isIndexType(): this is IndexType {
548            return !!(this.flags & TypeFlags.Index);
549        }
550        /**
551         * This polyfills `referenceType.typeArguments` for API consumers
552         */
553        get typeArguments() {
554            if (getObjectFlags(this) & ObjectFlags.Reference) {
555                return this.checker.getTypeArguments(this as Type as TypeReference);
556            }
557            return undefined;
558        }
559    }
560
561    class SignatureObject implements Signature {
562        flags: SignatureFlags;
563        checker: TypeChecker;
564        declaration!: SignatureDeclaration;
565        typeParameters?: TypeParameter[];
566        parameters!: Symbol[];
567        thisParameter!: Symbol;
568        resolvedReturnType!: Type;
569        resolvedTypePredicate: TypePredicate | undefined;
570        minTypeArgumentCount!: number;
571        minArgumentCount!: number;
572
573        // Undefined is used to indicate the value has not been computed. If, after computing, the
574        // symbol has no doc comment, then the empty array will be returned.
575        documentationComment?: SymbolDisplayPart[];
576        jsDocTags?: JSDocTagInfo[]; // same
577
578        constructor(checker: TypeChecker, flags: SignatureFlags) {
579            this.checker = checker;
580            this.flags = flags;
581        }
582
583        getDeclaration(): SignatureDeclaration {
584            return this.declaration;
585        }
586        getTypeParameters(): TypeParameter[] | undefined {
587            return this.typeParameters;
588        }
589        getParameters(): Symbol[] {
590            return this.parameters;
591        }
592        getReturnType(): Type {
593            return this.checker.getReturnTypeOfSignature(this);
594        }
595        getTypeParameterAtPosition(pos: number): Type {
596            const type = this.checker.getParameterType(this, pos);
597            if (type.isIndexType() && isThisTypeParameter(type.type)) {
598                const constraint = type.type.getConstraint();
599                if (constraint) {
600                    return this.checker.getIndexType(constraint);
601                }
602            }
603            return type;
604        }
605
606        getDocumentationComment(): SymbolDisplayPart[] {
607            return this.documentationComment || (this.documentationComment = getDocumentationComment(singleElementArray(this.declaration), this.checker));
608        }
609
610        getJsDocTags(): JSDocTagInfo[] {
611            return this.jsDocTags || (this.jsDocTags = getJsDocTagsOfDeclarations(singleElementArray(this.declaration), this.checker));
612        }
613    }
614
615    /**
616     * Returns whether or not the given node has a JSDoc "inheritDoc" tag on it.
617     * @param node the Node in question.
618     * @returns `true` if `node` has a JSDoc "inheritDoc" tag on it, otherwise `false`.
619     */
620    function hasJSDocInheritDocTag(node: Node) {
621        return getJSDocTags(node).some(tag => tag.tagName.text === "inheritDoc" || tag.tagName.text === "inheritdoc");
622    }
623
624    function getJsDocTagsOfDeclarations(declarations: Declaration[] | undefined, checker: TypeChecker | undefined): JSDocTagInfo[] {
625        if (!declarations) return emptyArray;
626
627        let tags = JsDoc.getJsDocTagsFromDeclarations(declarations, checker);
628        if (checker && (tags.length === 0 || declarations.some(hasJSDocInheritDocTag))) {
629            const seenSymbols = new Set<Symbol>();
630            for (const declaration of declarations) {
631                const inheritedTags = findBaseOfDeclaration(checker, declaration, symbol => {
632                    if (!seenSymbols.has(symbol)) {
633                        seenSymbols.add(symbol);
634                        if (declaration.kind === SyntaxKind.GetAccessor || declaration.kind === SyntaxKind.SetAccessor) {
635                            return symbol.getContextualJsDocTags(declaration, checker);
636                        }
637                        return symbol.declarations?.length === 1 ? symbol.getJsDocTags() : undefined;
638                    }
639                });
640                if (inheritedTags) {
641                    tags = [...inheritedTags, ...tags];
642                }
643            }
644        }
645        return tags;
646    }
647
648    function getDocumentationComment(declarations: readonly Declaration[] | undefined, checker: TypeChecker | undefined): SymbolDisplayPart[] {
649        if (!declarations) return emptyArray;
650
651        let doc = JsDoc.getJsDocCommentsFromDeclarations(declarations, checker);
652        if (checker && (doc.length === 0 || declarations.some(hasJSDocInheritDocTag))) {
653            const seenSymbols = new Set<Symbol>();
654            for (const declaration of declarations) {
655                const inheritedDocs = findBaseOfDeclaration(checker, declaration, symbol => {
656                    if (!seenSymbols.has(symbol)) {
657                        seenSymbols.add(symbol);
658                        if (declaration.kind === SyntaxKind.GetAccessor || declaration.kind === SyntaxKind.SetAccessor) {
659                            return symbol.getContextualDocumentationComment(declaration, checker);
660                        }
661                        return symbol.getDocumentationComment(checker);
662                    }
663                });
664                // TODO: GH#16312 Return a ReadonlyArray, avoid copying inheritedDocs
665                if (inheritedDocs) doc = doc.length === 0 ? inheritedDocs.slice() : inheritedDocs.concat(lineBreakPart(), doc);
666            }
667        }
668        return doc;
669    }
670
671    function findBaseOfDeclaration<T>(checker: TypeChecker, declaration: Declaration, cb: (symbol: Symbol) => T[] | undefined): T[] | undefined {
672        const classOrInterfaceDeclaration = declaration.parent?.kind === SyntaxKind.Constructor ? declaration.parent.parent : declaration.parent;
673        if (!classOrInterfaceDeclaration) return;
674
675        const isStaticMember = hasStaticModifier(declaration);
676        return firstDefined(getAllSuperTypeNodes(classOrInterfaceDeclaration), superTypeNode => {
677            const baseType = checker.getTypeAtLocation(superTypeNode);
678            const type = isStaticMember && baseType.symbol ? checker.getTypeOfSymbol(baseType.symbol) : baseType;
679            const symbol = checker.getPropertyOfType(type, declaration.symbol.name);
680            return symbol ? cb(symbol) : undefined;
681        });
682    }
683
684    class SourceFileObject extends NodeObject implements SourceFile {
685        public kind: SyntaxKind.SourceFile = SyntaxKind.SourceFile;
686        public _declarationBrand: any;
687        public fileName!: string;
688        public path!: Path;
689        public resolvedPath!: Path;
690        public originalFileName!: string;
691        public text!: string;
692        public scriptSnapshot!: IScriptSnapshot;
693        public lineMap!: readonly number[];
694
695        public statements!: NodeArray<Statement>;
696        public endOfFileToken!: Token<SyntaxKind.EndOfFileToken>;
697
698        public amdDependencies!: { name: string; path: string }[];
699        public moduleName!: string;
700        public referencedFiles!: FileReference[];
701        public typeReferenceDirectives!: FileReference[];
702        public libReferenceDirectives!: FileReference[];
703
704        public syntacticDiagnostics!: DiagnosticWithLocation[];
705        public parseDiagnostics!: DiagnosticWithLocation[];
706        public bindDiagnostics!: DiagnosticWithLocation[];
707        public bindSuggestionDiagnostics?: DiagnosticWithLocation[];
708
709        public isDeclarationFile!: boolean;
710        public isDefaultLib!: boolean;
711        public hasNoDefaultLib!: boolean;
712        public externalModuleIndicator!: Node; // The first node that causes this file to be an external module
713        public commonJsModuleIndicator!: Node; // The first node that causes this file to be a CommonJS module
714        public nodeCount!: number;
715        public identifierCount!: number;
716        public symbolCount!: number;
717        public version!: string;
718        public scriptKind!: ScriptKind;
719        public languageVersion!: ScriptTarget;
720        public languageVariant!: LanguageVariant;
721        public identifiers!: ESMap<string, string>;
722        public nameTable: UnderscoreEscapedMap<number> | undefined;
723        public resolvedModules: ModeAwareCache<ResolvedModuleFull> | undefined;
724        public resolvedTypeReferenceDirectiveNames!: ModeAwareCache<ResolvedTypeReferenceDirective>;
725        public imports!: readonly StringLiteralLike[];
726        public moduleAugmentations!: StringLiteral[];
727        private namedDeclarations: ESMap<string, Declaration[]> | undefined;
728        public ambientModuleNames!: string[];
729        public checkJsDirective: CheckJsDirective | undefined;
730        public errorExpectations: TextRange[] | undefined;
731        public possiblyContainDynamicImport?: boolean;
732        public pragmas!: PragmaMap;
733        public localJsxFactory: EntityName | undefined;
734        public localJsxNamespace: __String | undefined;
735
736        constructor(kind: SyntaxKind, pos: number, end: number) {
737            super(kind, pos, end);
738        }
739
740        public update(newText: string, textChangeRange: TextChangeRange): SourceFile {
741            return updateSourceFile(this, newText, textChangeRange);
742        }
743
744        public getLineAndCharacterOfPosition(position: number): LineAndCharacter {
745            return getLineAndCharacterOfPosition(this, position);
746        }
747
748        public getLineStarts(): readonly number[] {
749            return getLineStarts(this);
750        }
751
752        public getPositionOfLineAndCharacter(line: number, character: number, allowEdits?: true): number {
753            return computePositionOfLineAndCharacter(getLineStarts(this), line, character, this.text, allowEdits);
754        }
755
756        public getLineEndOfPosition(pos: number): number {
757            const { line } = this.getLineAndCharacterOfPosition(pos);
758            const lineStarts = this.getLineStarts();
759
760            let lastCharPos: number | undefined;
761            if (line + 1 >= lineStarts.length) {
762                lastCharPos = this.getEnd();
763            }
764            if (!lastCharPos) {
765                lastCharPos = lineStarts[line + 1] - 1;
766            }
767
768            const fullText = this.getFullText();
769            // if the new line is "\r\n", we should return the last non-new-line-character position
770            return fullText[lastCharPos] === "\n" && fullText[lastCharPos - 1] === "\r" ? lastCharPos - 1 : lastCharPos;
771        }
772
773        public getNamedDeclarations(): ESMap<string, Declaration[]> {
774            if (!this.namedDeclarations) {
775                this.namedDeclarations = this.computeNamedDeclarations();
776            }
777
778            return this.namedDeclarations;
779        }
780
781        private computeNamedDeclarations(): ESMap<string, Declaration[]> {
782            const result = createMultiMap<Declaration>();
783
784            this.forEachChild(visit);
785
786            return result;
787
788            function addDeclaration(declaration: Declaration) {
789                const name = getDeclarationName(declaration);
790                if (name) {
791                    result.add(name, declaration);
792                }
793            }
794
795            function getDeclarations(name: string) {
796                let declarations = result.get(name);
797                if (!declarations) {
798                    result.set(name, declarations = []);
799                }
800                return declarations;
801            }
802
803            function getDeclarationName(declaration: Declaration) {
804                const name = getNonAssignedNameOfDeclaration(declaration);
805                return name && (isComputedPropertyName(name) && isPropertyAccessExpression(name.expression) ? name.expression.name.text
806                    : isPropertyName(name) ? getNameFromPropertyName(name) : undefined);
807            }
808
809            function visit(node: Node): void {
810                switch (node.kind) {
811                    case SyntaxKind.FunctionDeclaration:
812                    case SyntaxKind.FunctionExpression:
813                    case SyntaxKind.MethodDeclaration:
814                    case SyntaxKind.MethodSignature:
815                        const functionDeclaration = node as FunctionLikeDeclaration;
816                        const declarationName = getDeclarationName(functionDeclaration);
817
818                        if (declarationName) {
819                            const declarations = getDeclarations(declarationName);
820                            const lastDeclaration = lastOrUndefined(declarations);
821
822                            // Check whether this declaration belongs to an "overload group".
823                            if (lastDeclaration && functionDeclaration.parent === lastDeclaration.parent && functionDeclaration.symbol === lastDeclaration.symbol) {
824                                // Overwrite the last declaration if it was an overload
825                                // and this one is an implementation.
826                                if (functionDeclaration.body && !(lastDeclaration as FunctionLikeDeclaration).body) {
827                                    declarations[declarations.length - 1] = functionDeclaration;
828                                }
829                            }
830                            else {
831                                declarations.push(functionDeclaration);
832                            }
833                        }
834                        forEachChild(node, visit);
835                        break;
836
837                    case SyntaxKind.ClassDeclaration:
838                    case SyntaxKind.ClassExpression:
839                    case SyntaxKind.StructDeclaration:
840                    case SyntaxKind.InterfaceDeclaration:
841                    case SyntaxKind.TypeAliasDeclaration:
842                    case SyntaxKind.EnumDeclaration:
843                    case SyntaxKind.ModuleDeclaration:
844                    case SyntaxKind.ImportEqualsDeclaration:
845                    case SyntaxKind.ExportSpecifier:
846                    case SyntaxKind.ImportSpecifier:
847                    case SyntaxKind.ImportClause:
848                    case SyntaxKind.NamespaceImport:
849                    case SyntaxKind.GetAccessor:
850                    case SyntaxKind.SetAccessor:
851                    case SyntaxKind.TypeLiteral:
852                        addDeclaration(node as Declaration);
853                        forEachChild(node, visit);
854                        break;
855
856                    case SyntaxKind.Parameter:
857                        // Only consider parameter properties
858                        if (!hasSyntacticModifier(node, ModifierFlags.ParameterPropertyModifier)) {
859                            break;
860                        }
861                    // falls through
862
863                    case SyntaxKind.VariableDeclaration:
864                    case SyntaxKind.BindingElement: {
865                        const decl = node as VariableDeclaration;
866                        if (isBindingPattern(decl.name)) {
867                            forEachChild(decl.name, visit);
868                            break;
869                        }
870                        if (decl.initializer) {
871                            visit(decl.initializer);
872                        }
873                    }
874                    // falls through
875                    case SyntaxKind.EnumMember:
876                    case SyntaxKind.PropertyDeclaration:
877                    case SyntaxKind.PropertySignature:
878                        addDeclaration(node as Declaration);
879                        break;
880
881                    case SyntaxKind.ExportDeclaration:
882                        // Handle named exports case e.g.:
883                        //    export {a, b as B} from "mod";
884                        const exportDeclaration = node as ExportDeclaration;
885                        if (exportDeclaration.exportClause) {
886                            if (isNamedExports(exportDeclaration.exportClause)) {
887                                forEach(exportDeclaration.exportClause.elements, visit);
888                            }
889                            else {
890                                visit(exportDeclaration.exportClause.name);
891                            }
892                        }
893                        break;
894
895                    case SyntaxKind.ImportDeclaration:
896                        const importClause = (node as ImportDeclaration).importClause;
897                        if (importClause) {
898                            // Handle default import case e.g.:
899                            //    import d from "mod";
900                            if (importClause.name) {
901                                addDeclaration(importClause.name);
902                            }
903
904                            // Handle named bindings in imports e.g.:
905                            //    import * as NS from "mod";
906                            //    import {a, b as B} from "mod";
907                            if (importClause.namedBindings) {
908                                if (importClause.namedBindings.kind === SyntaxKind.NamespaceImport) {
909                                    addDeclaration(importClause.namedBindings);
910                                }
911                                else {
912                                    forEach(importClause.namedBindings.elements, visit);
913                                }
914                            }
915                        }
916                        break;
917
918                    case SyntaxKind.BinaryExpression:
919                        if (getAssignmentDeclarationKind(node as BinaryExpression) !== AssignmentDeclarationKind.None) {
920                            addDeclaration(node as BinaryExpression);
921                        }
922                    // falls through
923
924                    default:
925                        forEachChild(node, visit);
926                }
927            }
928        }
929    }
930
931    class SourceMapSourceObject implements SourceMapSource {
932        lineMap!: number[];
933        constructor(public fileName: string, public text: string, public skipTrivia?: (pos: number) => number) { }
934
935        public getLineAndCharacterOfPosition(pos: number): LineAndCharacter {
936            return getLineAndCharacterOfPosition(this, pos);
937        }
938    }
939
940    function getServicesObjectAllocator(): ObjectAllocator {
941        return {
942            getNodeConstructor: () => NodeObject,
943            getTokenConstructor: () => TokenObject,
944
945            getIdentifierConstructor: () => IdentifierObject,
946            getPrivateIdentifierConstructor: () => PrivateIdentifierObject,
947            getSourceFileConstructor: () => SourceFileObject,
948            getSymbolConstructor: () => SymbolObject,
949            getTypeConstructor: () => TypeObject,
950            getSignatureConstructor: () => SignatureObject,
951            getSourceMapSourceConstructor: () => SourceMapSourceObject,
952        };
953    }
954
955    /// Language Service
956
957    /* @internal */
958    export interface DisplayPartsSymbolWriter extends EmitTextWriter {
959        displayParts(): SymbolDisplayPart[];
960    }
961
962    /* @internal */
963    export function toEditorSettings(options: FormatCodeOptions | FormatCodeSettings): FormatCodeSettings;
964    export function toEditorSettings(options: EditorOptions | EditorSettings): EditorSettings;
965    export function toEditorSettings(optionsAsMap: MapLike<any>): MapLike<any> {
966        let allPropertiesAreCamelCased = true;
967        for (const key in optionsAsMap) {
968            if (hasProperty(optionsAsMap, key) && !isCamelCase(key)) {
969                allPropertiesAreCamelCased = false;
970                break;
971            }
972        }
973        if (allPropertiesAreCamelCased) {
974            return optionsAsMap;
975        }
976        const settings: MapLike<any> = {};
977        for (const key in optionsAsMap) {
978            if (hasProperty(optionsAsMap, key)) {
979                const newKey = isCamelCase(key) ? key : key.charAt(0).toLowerCase() + key.substr(1);
980                settings[newKey] = optionsAsMap[key];
981            }
982        }
983        return settings;
984    }
985
986    function isCamelCase(s: string) {
987        return !s.length || s.charAt(0) === s.charAt(0).toLowerCase();
988    }
989
990    export function displayPartsToString(displayParts: SymbolDisplayPart[] | undefined) {
991        if (displayParts) {
992            return map(displayParts, displayPart => displayPart.text).join("");
993        }
994
995        return "";
996    }
997
998    export function getDefaultCompilerOptions(): CompilerOptions {
999        // Always default to "ScriptTarget.ES5" for the language service
1000        return {
1001            target: ScriptTarget.ES5,
1002            jsx: JsxEmit.Preserve
1003        };
1004    }
1005
1006    export function getSupportedCodeFixes() {
1007        return codefix.getSupportedErrorCodes();
1008    }
1009
1010    class SyntaxTreeCache {
1011        // For our syntactic only features, we also keep a cache of the syntax tree for the
1012        // currently edited file.
1013        private currentFileName: string | undefined;
1014        private currentFileVersion: string | undefined;
1015        private currentFileScriptSnapshot: IScriptSnapshot | undefined;
1016        private currentSourceFile: SourceFile | undefined;
1017
1018        constructor(private host: LanguageServiceHost) {
1019        }
1020
1021        public getCurrentSourceFile(fileName: string): SourceFile {
1022            const scriptSnapshot = this.host.getScriptSnapshot(fileName);
1023            if (!scriptSnapshot) {
1024                // The host does not know about this file.
1025                throw new Error("Could not find file: '" + fileName + "'.");
1026            }
1027
1028            const scriptKind = getScriptKind(fileName, this.host);
1029            const version = this.host.getScriptVersion(fileName);
1030            let sourceFile: SourceFile | undefined;
1031
1032            if (this.currentFileName !== fileName) {
1033                // This is a new file, just parse it
1034                const options: CreateSourceFileOptions = {
1035                    languageVersion: ScriptTarget.Latest,
1036                    impliedNodeFormat: getImpliedNodeFormatForFile(
1037                        toPath(fileName, this.host.getCurrentDirectory(), this.host.getCompilerHost?.()?.getCanonicalFileName || hostGetCanonicalFileName(this.host)),
1038                        this.host.getCompilerHost?.()?.getModuleResolutionCache?.()?.getPackageJsonInfoCache(),
1039                        this.host,
1040                        this.host.getCompilationSettings()
1041                    ),
1042                    setExternalModuleIndicator: getSetExternalModuleIndicator(this.host.getCompilationSettings())
1043                };
1044                sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, options, version, /*setNodeParents*/ true, scriptKind, this.host.getCompilationSettings());
1045            }
1046            else if (this.currentFileVersion !== version) {
1047                // This is the same file, just a newer version. Incrementally parse the file.
1048                const editRange = scriptSnapshot.getChangeRange(this.currentFileScriptSnapshot!);
1049                sourceFile = updateLanguageServiceSourceFile(this.currentSourceFile!, scriptSnapshot, version, editRange, /*aggressiveChecks*/ undefined, this.host.getCompilationSettings());
1050            }
1051
1052            if (sourceFile) {
1053                // All done, ensure state is up to date
1054                this.currentFileVersion = version;
1055                this.currentFileName = fileName;
1056                this.currentFileScriptSnapshot = scriptSnapshot;
1057                this.currentSourceFile = sourceFile;
1058            }
1059
1060            return this.currentSourceFile!;
1061        }
1062    }
1063
1064    function setSourceFileFields(sourceFile: SourceFile, scriptSnapshot: IScriptSnapshot, version: string) {
1065        sourceFile.version = version;
1066        sourceFile.scriptSnapshot = scriptSnapshot;
1067    }
1068
1069    export function createLanguageServiceSourceFile(fileName: string, scriptSnapshot: IScriptSnapshot, scriptTargetOrOptions: ScriptTarget | CreateSourceFileOptions, version: string, setNodeParents: boolean, scriptKind?: ScriptKind, option?: CompilerOptions): SourceFile {
1070        const sourceFile = createSourceFile(fileName, getSnapshotText(scriptSnapshot), scriptTargetOrOptions, setNodeParents, scriptKind, option);
1071        setSourceFileFields(sourceFile, scriptSnapshot, version);
1072        return sourceFile;
1073    }
1074
1075    export function updateLanguageServiceSourceFile(sourceFile: SourceFile, scriptSnapshot: IScriptSnapshot, version: string, textChangeRange: TextChangeRange | undefined, aggressiveChecks?: boolean, option?: CompilerOptions): SourceFile {
1076        // If we were given a text change range, and our version or open-ness changed, then
1077        // incrementally parse this file.
1078        if (textChangeRange) {
1079            if (version !== sourceFile.version) {
1080                let newText: string;
1081
1082                // grab the fragment from the beginning of the original text to the beginning of the span
1083                const prefix = textChangeRange.span.start !== 0
1084                    ? sourceFile.text.substr(0, textChangeRange.span.start)
1085                    : "";
1086
1087                // grab the fragment from the end of the span till the end of the original text
1088                const suffix = textSpanEnd(textChangeRange.span) !== sourceFile.text.length
1089                    ? sourceFile.text.substr(textSpanEnd(textChangeRange.span))
1090                    : "";
1091
1092                if (textChangeRange.newLength === 0) {
1093                    // edit was a deletion - just combine prefix and suffix
1094                    newText = prefix && suffix ? prefix + suffix : prefix || suffix;
1095                }
1096                else {
1097                    // it was actual edit, fetch the fragment of new text that correspond to new span
1098                    const changedText = scriptSnapshot.getText(textChangeRange.span.start, textChangeRange.span.start + textChangeRange.newLength);
1099                    // combine prefix, changed text and suffix
1100                    newText = prefix && suffix
1101                        ? prefix + changedText + suffix
1102                        : prefix
1103                            ? (prefix + changedText)
1104                            : (changedText + suffix);
1105                }
1106
1107                const newSourceFile = updateSourceFile(sourceFile, newText, textChangeRange, aggressiveChecks, option);
1108                setSourceFileFields(newSourceFile, scriptSnapshot, version);
1109                // after incremental parsing nameTable might not be up-to-date
1110                // drop it so it can be lazily recreated later
1111                newSourceFile.nameTable = undefined;
1112
1113                // dispose all resources held by old script snapshot
1114                if (sourceFile !== newSourceFile && sourceFile.scriptSnapshot) {
1115                    if (sourceFile.scriptSnapshot.dispose) {
1116                        sourceFile.scriptSnapshot.dispose();
1117                    }
1118
1119                    sourceFile.scriptSnapshot = undefined;
1120                }
1121
1122                return newSourceFile;
1123            }
1124        }
1125
1126        const options: CreateSourceFileOptions = {
1127            languageVersion: sourceFile.languageVersion,
1128            impliedNodeFormat: sourceFile.impliedNodeFormat,
1129            setExternalModuleIndicator: sourceFile.setExternalModuleIndicator,
1130        };
1131        // Otherwise, just create a new source file.
1132        return createLanguageServiceSourceFile(sourceFile.fileName, scriptSnapshot, options, version, /*setNodeParents*/ true, sourceFile.scriptKind, option);
1133    }
1134
1135    const NoopCancellationToken: CancellationToken = {
1136        isCancellationRequested: returnFalse,
1137        throwIfCancellationRequested: noop,
1138    };
1139
1140    class CancellationTokenObject implements CancellationToken {
1141        constructor(private cancellationToken: HostCancellationToken) {
1142        }
1143
1144        public isCancellationRequested(): boolean {
1145            return this.cancellationToken.isCancellationRequested();
1146        }
1147
1148        public throwIfCancellationRequested(): void {
1149            if (this.isCancellationRequested()) {
1150                tracing?.instant(tracing.Phase.Session, "cancellationThrown", { kind: "CancellationTokenObject" });
1151                throw new OperationCanceledException();
1152            }
1153        }
1154    }
1155
1156    /* @internal */
1157    /** A cancellation that throttles calls to the host */
1158    export class ThrottledCancellationToken implements CancellationToken {
1159        // Store when we last tried to cancel.  Checking cancellation can be expensive (as we have
1160        // to marshall over to the host layer).  So we only bother actually checking once enough
1161        // time has passed.
1162        private lastCancellationCheckTime = 0;
1163
1164        constructor(private hostCancellationToken: HostCancellationToken, private readonly throttleWaitMilliseconds = 20) {
1165        }
1166
1167        public isCancellationRequested(): boolean {
1168            const time = timestamp();
1169            const duration = Math.abs(time - this.lastCancellationCheckTime);
1170            if (duration >= this.throttleWaitMilliseconds) {
1171                // Check no more than once every throttle wait milliseconds
1172                this.lastCancellationCheckTime = time;
1173                return this.hostCancellationToken.isCancellationRequested();
1174            }
1175
1176            return false;
1177        }
1178
1179        public throwIfCancellationRequested(): void {
1180            if (this.isCancellationRequested()) {
1181                tracing?.instant(tracing.Phase.Session, "cancellationThrown", { kind: "ThrottledCancellationToken" });
1182                throw new OperationCanceledException();
1183            }
1184        }
1185    }
1186
1187    const invalidOperationsInPartialSemanticMode: readonly (keyof LanguageService)[] = [
1188        "getSemanticDiagnostics",
1189        "getSuggestionDiagnostics",
1190        "getCompilerOptionsDiagnostics",
1191        "getSemanticClassifications",
1192        "getEncodedSemanticClassifications",
1193        "getCodeFixesAtPosition",
1194        "getCombinedCodeFix",
1195        "applyCodeActionCommand",
1196        "organizeImports",
1197        "getEditsForFileRename",
1198        "getEmitOutput",
1199        "getApplicableRefactors",
1200        "getEditsForRefactor",
1201        "prepareCallHierarchy",
1202        "provideCallHierarchyIncomingCalls",
1203        "provideCallHierarchyOutgoingCalls",
1204        "provideInlayHints"
1205    ];
1206
1207    const invalidOperationsInSyntacticMode: readonly (keyof LanguageService)[] = [
1208        ...invalidOperationsInPartialSemanticMode,
1209        "getCompletionsAtPosition",
1210        "getCompletionEntryDetails",
1211        "getCompletionEntrySymbol",
1212        "getSignatureHelpItems",
1213        "getQuickInfoAtPosition",
1214        "getDefinitionAtPosition",
1215        "getDefinitionAndBoundSpan",
1216        "getImplementationAtPosition",
1217        "getTypeDefinitionAtPosition",
1218        "getReferencesAtPosition",
1219        "findReferences",
1220        "getOccurrencesAtPosition",
1221        "getDocumentHighlights",
1222        "getNavigateToItems",
1223        "getRenameInfo",
1224        "findRenameLocations",
1225        "getApplicableRefactors",
1226    ];
1227    export function createLanguageService(
1228        host: LanguageServiceHost,
1229        documentRegistry: DocumentRegistry = createDocumentRegistry(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames(), host.getCurrentDirectory()),
1230        syntaxOnlyOrLanguageServiceMode?: boolean | LanguageServiceMode,
1231    ): LanguageService {
1232        let languageServiceMode: LanguageServiceMode;
1233        if (syntaxOnlyOrLanguageServiceMode === undefined) {
1234            languageServiceMode = LanguageServiceMode.Semantic;
1235        }
1236        else if (typeof syntaxOnlyOrLanguageServiceMode === "boolean") {
1237            // languageServiceMode = SyntaxOnly
1238            languageServiceMode = syntaxOnlyOrLanguageServiceMode ? LanguageServiceMode.Syntactic : LanguageServiceMode.Semantic;
1239        }
1240        else {
1241            languageServiceMode = syntaxOnlyOrLanguageServiceMode;
1242        }
1243
1244        const syntaxTreeCache: SyntaxTreeCache = new SyntaxTreeCache(host);
1245        let program: Program;
1246        let builderProgram: BuilderProgram;
1247        let lastProjectVersion: string;
1248        let lastTypesRootVersion = 0;
1249
1250        const cancellationToken = host.getCancellationToken
1251            ? new CancellationTokenObject(host.getCancellationToken())
1252            : NoopCancellationToken;
1253
1254        const currentDirectory = host.getCurrentDirectory();
1255
1256        // Checks if the localized messages json is set, and if not, query the host for it
1257        maybeSetLocalizedDiagnosticMessages(host.getLocalizedDiagnosticMessages?.bind(host));
1258
1259        function log(message: string) {
1260            if (host.log) {
1261                host.log(message);
1262            }
1263        }
1264
1265        const useCaseSensitiveFileNames = hostUsesCaseSensitiveFileNames(host);
1266        const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
1267
1268        const sourceMapper = getSourceMapper({
1269            useCaseSensitiveFileNames: () => useCaseSensitiveFileNames,
1270            getCurrentDirectory: () => currentDirectory,
1271            getProgram,
1272            fileExists: maybeBind(host, host.fileExists),
1273            readFile: maybeBind(host, host.readFile),
1274            getDocumentPositionMapper: maybeBind(host, host.getDocumentPositionMapper),
1275            getSourceFileLike: maybeBind(host, host.getSourceFileLike),
1276            log
1277        });
1278
1279        function getValidSourceFile(fileName: string): SourceFile {
1280            const sourceFile = program.getSourceFile(fileName);
1281            if (!sourceFile) {
1282                const error: Error & PossibleProgramFileInfo = new Error(`Could not find source file: '${fileName}'.`);
1283
1284                // We've been having trouble debugging this, so attach sidecar data for the tsserver log.
1285                // See https://github.com/microsoft/TypeScript/issues/30180.
1286                error.ProgramFiles = program.getSourceFiles().map(f => f.fileName);
1287
1288                throw error;
1289            }
1290            return sourceFile;
1291        }
1292
1293        function synchronizeHostData(isCreateIncrementalProgramMode: boolean = false, withLinterProgram?: boolean): void {
1294            Debug.assert(languageServiceMode !== LanguageServiceMode.Syntactic);
1295            // perform fast check if host supports it
1296            if (host.getProjectVersion) {
1297                const hostProjectVersion = host.getProjectVersion();
1298                if (hostProjectVersion) {
1299                    if (lastProjectVersion === hostProjectVersion && !host.hasChangedAutomaticTypeDirectiveNames?.()) {
1300                        return;
1301                    }
1302
1303                    lastProjectVersion = hostProjectVersion;
1304                }
1305            }
1306
1307            const typeRootsVersion = host.getTypeRootsVersion ? host.getTypeRootsVersion() : 0;
1308            if (lastTypesRootVersion !== typeRootsVersion) {
1309                log("TypeRoots version has changed; provide new program");
1310                program = undefined!; // TODO: GH#18217
1311                lastTypesRootVersion = typeRootsVersion;
1312            }
1313
1314            // This array is retained by the program and will be used to determine if the program is up to date,
1315            // so we need to make a copy in case the host mutates the underlying array - otherwise it would look
1316            // like every program always has the host's current list of root files.
1317            const rootFileNames = host.getScriptFileNames().slice();
1318
1319            // Get a fresh cache of the host information
1320            const newSettings = host.getCompilationSettings() || getDefaultCompilerOptions();
1321            const hasInvalidatedResolutions: HasInvalidatedResolutions = host.hasInvalidatedResolutions || returnFalse;
1322            const hasChangedAutomaticTypeDirectiveNames = maybeBind(host, host.hasChangedAutomaticTypeDirectiveNames);
1323            const projectReferences = host.getProjectReferences?.();
1324            let parsedCommandLines: ESMap<Path, ParsedCommandLine | false> | undefined;
1325
1326            // Now create a new compiler
1327            let compilerHost: CompilerHost | undefined = {
1328                getSourceFile: getOrCreateSourceFile,
1329                getSourceFileByPath: getOrCreateSourceFileByPath,
1330                getCancellationToken: () => cancellationToken,
1331                getCanonicalFileName,
1332                useCaseSensitiveFileNames: () => useCaseSensitiveFileNames,
1333                getNewLine: () => getNewLineCharacter(newSettings, () => getNewLineOrDefaultFromHost(host)),
1334                getDefaultLibFileName: options => host.getDefaultLibFileName(options),
1335                writeFile: noop,
1336                getCurrentDirectory: () => currentDirectory,
1337                fileExists: fileName => host.fileExists(fileName),
1338                readFile: fileName => host.readFile && host.readFile(fileName),
1339                getSymlinkCache: maybeBind(host, host.getSymlinkCache),
1340                realpath: maybeBind(host, host.realpath),
1341                directoryExists: directoryName => {
1342                    return directoryProbablyExists(directoryName, host);
1343                },
1344                getDirectories: path => {
1345                    return host.getDirectories ? host.getDirectories(path) : [];
1346                },
1347                readDirectory: (path: string, extensions?: readonly string[], exclude?: readonly string[], include?: readonly string[], depth?: number) => {
1348                    Debug.checkDefined(host.readDirectory, "'LanguageServiceHost.readDirectory' must be implemented to correctly process 'projectReferences'");
1349                    return host.readDirectory!(path, extensions, exclude, include, depth);
1350                },
1351                onReleaseOldSourceFile,
1352                onReleaseParsedCommandLine,
1353                hasInvalidatedResolutions,
1354                hasChangedAutomaticTypeDirectiveNames,
1355                trace: maybeBind(host, host.trace),
1356                resolveModuleNames: maybeBind(host, host.resolveModuleNames),
1357                getModuleResolutionCache: maybeBind(host, host.getModuleResolutionCache),
1358                resolveTypeReferenceDirectives: maybeBind(host, host.resolveTypeReferenceDirectives),
1359                useSourceOfProjectReferenceRedirect: maybeBind(host, host.useSourceOfProjectReferenceRedirect),
1360                getParsedCommandLine,
1361                getJsDocNodeCheckedConfig: maybeBind(host, host.getJsDocNodeCheckedConfig),
1362                getJsDocNodeConditionCheckedResult: maybeBind(host, host.getJsDocNodeConditionCheckedResult),
1363                getFileCheckedModuleInfo: maybeBind(host, host.getFileCheckedModuleInfo),
1364            };
1365
1366            const originalGetSourceFile = compilerHost.getSourceFile;
1367
1368            const { getSourceFileWithCache } = changeCompilerHostLikeToUseCache(
1369                compilerHost,
1370                fileName => toPath(fileName, currentDirectory, getCanonicalFileName),
1371                (...args) => originalGetSourceFile.call(compilerHost, ...args)
1372            );
1373            compilerHost.getSourceFile = getSourceFileWithCache!;
1374
1375            host.setCompilerHost?.(compilerHost);
1376
1377            const parseConfigHost: ParseConfigFileHost = {
1378                useCaseSensitiveFileNames,
1379                fileExists: fileName => compilerHost!.fileExists(fileName),
1380                readFile: fileName => compilerHost!.readFile(fileName),
1381                readDirectory: (...args) => compilerHost!.readDirectory!(...args),
1382                trace: compilerHost.trace,
1383                getCurrentDirectory: compilerHost.getCurrentDirectory,
1384                onUnRecoverableConfigFileDiagnostic: noop,
1385            };
1386
1387            // The call to isProgramUptoDate below may refer back to documentRegistryBucketKey;
1388            // calculate this early so it's not undefined if downleveled to a var (or, if emitted
1389            // as a const variable without downleveling, doesn't crash).
1390            const documentRegistryBucketKey = documentRegistry.getKeyForCompilationSettings(newSettings);
1391
1392            // If the program is already up-to-date, we can reuse it
1393            if (isProgramUptoDate(program, rootFileNames, newSettings, (_path, fileName) => host.getScriptVersion(fileName), fileName => compilerHost!.fileExists(fileName), hasInvalidatedResolutions, hasChangedAutomaticTypeDirectiveNames, getParsedCommandLine, projectReferences)) {
1394                return;
1395            }
1396
1397            // clear ArkUI collected properties
1398            host.clearProps && host.clearProps();
1399
1400            compilerHost.getLastCompiledProgram = () => { return program; }
1401
1402            // IMPORTANT - It is critical from this moment onward that we do not check
1403            // cancellation tokens.  We are about to mutate source files from a previous program
1404            // instance.  If we cancel midway through, we may end up in an inconsistent state where
1405            // the program points to old source files that have been invalidated because of
1406            // incremental parsing.
1407
1408            const options: CreateProgramOptions = {
1409                rootNames: rootFileNames,
1410                options: newSettings,
1411                host: compilerHost,
1412                oldProgram: program,
1413                projectReferences
1414            };
1415
1416            if (isCreateIncrementalProgramMode) {
1417                if (withLinterProgram) {
1418                    builderProgram = createIncrementalProgramForArkTs(options);
1419                } else {
1420                    builderProgram = createIncrementalProgram(options);
1421                }
1422                program = builderProgram.getProgram();
1423            } else {
1424                program = createProgram(options);
1425            }
1426
1427            // 'getOrCreateSourceFile' depends on caching but should be used past this point.
1428            // After this point, the cache needs to be cleared to allow all collected snapshots to be released
1429            compilerHost = undefined;
1430            parsedCommandLines = undefined;
1431
1432            // We reset this cache on structure invalidation so we don't hold on to outdated files for long; however we can't use the `compilerHost` above,
1433            // Because it only functions until `hostCache` is cleared, while we'll potentially need the functionality to lazily read sourcemap files during
1434            // the course of whatever called `synchronizeHostData`
1435            sourceMapper.clearCache();
1436
1437            // Make sure all the nodes in the program are both bound, and have their parent
1438            // pointers set property.
1439            program.getTypeChecker();
1440            return;
1441
1442            function getParsedCommandLine(fileName: string): ParsedCommandLine | undefined {
1443                const path = toPath(fileName, currentDirectory, getCanonicalFileName);
1444                const existing = parsedCommandLines?.get(path);
1445                if (existing !== undefined) return existing || undefined;
1446
1447                const result = host.getParsedCommandLine ?
1448                    host.getParsedCommandLine(fileName) :
1449                    getParsedCommandLineOfConfigFileUsingSourceFile(fileName);
1450                (parsedCommandLines ||= new Map()).set(path, result || false);
1451                return result;
1452            }
1453
1454            function getParsedCommandLineOfConfigFileUsingSourceFile(configFileName: string): ParsedCommandLine | undefined {
1455                const result = getOrCreateSourceFile(configFileName, ScriptTarget.JSON) as JsonSourceFile | undefined;
1456                if (!result) return undefined;
1457                result.path = toPath(configFileName, currentDirectory, getCanonicalFileName);
1458                result.resolvedPath = result.path;
1459                result.originalFileName = result.fileName;
1460                return parseJsonSourceFileConfigFileContent(
1461                    result,
1462                    parseConfigHost,
1463                    getNormalizedAbsolutePath(getDirectoryPath(configFileName), currentDirectory),
1464                    /*optionsToExtend*/ undefined,
1465                    getNormalizedAbsolutePath(configFileName, currentDirectory),
1466                );
1467            }
1468
1469            function onReleaseParsedCommandLine(configFileName: string, oldResolvedRef: ResolvedProjectReference | undefined, oldOptions: CompilerOptions) {
1470                if (host.getParsedCommandLine) {
1471                    host.onReleaseParsedCommandLine?.(configFileName, oldResolvedRef, oldOptions);
1472                }
1473                else if (oldResolvedRef) {
1474                    onReleaseOldSourceFile(oldResolvedRef.sourceFile, oldOptions);
1475                }
1476            }
1477
1478            // Release any files we have acquired in the old program but are
1479            // not part of the new program.
1480            function onReleaseOldSourceFile(oldSourceFile: SourceFile, oldOptions: CompilerOptions) {
1481                const oldSettingsKey = documentRegistry.getKeyForCompilationSettings(oldOptions);
1482                documentRegistry.releaseDocumentWithKey(oldSourceFile.resolvedPath, oldSettingsKey, oldSourceFile.scriptKind, oldSourceFile.impliedNodeFormat);
1483            }
1484
1485            function getOrCreateSourceFile(fileName: string, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined {
1486                return getOrCreateSourceFileByPath(fileName, toPath(fileName, currentDirectory, getCanonicalFileName), languageVersionOrOptions, onError, shouldCreateNewSourceFile);
1487            }
1488
1489            function getOrCreateSourceFileByPath(fileName: string, path: Path, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, _onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined {
1490                Debug.assert(compilerHost, "getOrCreateSourceFileByPath called after typical CompilerHost lifetime, check the callstack something with a reference to an old host.");
1491                // The program is asking for this file, check first if the host can locate it.
1492                // If the host can not locate the file, then it does not exist. return undefined
1493                // to the program to allow reporting of errors for missing files.
1494                const scriptSnapshot = host.getScriptSnapshot(fileName);
1495                if (!scriptSnapshot) {
1496                    return undefined;
1497                }
1498
1499                const scriptKind = getScriptKind(fileName, host);
1500                const scriptVersion = host.getScriptVersion(fileName);
1501
1502                // Check if the language version has changed since we last created a program; if they are the same,
1503                // it is safe to reuse the sourceFiles; if not, then the shape of the AST can change, and the oldSourceFile
1504                // can not be reused. we have to dump all syntax trees and create new ones.
1505                if (!shouldCreateNewSourceFile) {
1506                    // Check if the old program had this file already
1507                    const oldSourceFile = program && program.getSourceFileByPath(path);
1508                    if (oldSourceFile) {
1509                        // We already had a source file for this file name.  Go to the registry to
1510                        // ensure that we get the right up to date version of it.  We need this to
1511                        // address the following race-condition.  Specifically, say we have the following:
1512                        //
1513                        //      LS1
1514                        //          \
1515                        //           DocumentRegistry
1516                        //          /
1517                        //      LS2
1518                        //
1519                        // Each LS has a reference to file 'foo.ts' at version 1.  LS2 then updates
1520                        // it's version of 'foo.ts' to version 2.  This will cause LS2 and the
1521                        // DocumentRegistry to have version 2 of the document.  However, LS1 will
1522                        // have version 1.  And *importantly* this source file will be *corrupt*.
1523                        // The act of creating version 2 of the file irrevocably damages the version
1524                        // 1 file.
1525                        //
1526                        // So, later when we call into LS1, we need to make sure that it doesn't use
1527                        // it's source file any more, and instead defers to DocumentRegistry to get
1528                        // either version 1, version 2 (or some other version) depending on what the
1529                        // host says should be used.
1530
1531                        // We do not support the scenario where a host can modify a registered
1532                        // file's script kind, i.e. in one project some file is treated as ".ts"
1533                        // and in another as ".js"
1534                        if (scriptKind === oldSourceFile.scriptKind) {
1535                            return documentRegistry.updateDocumentWithKey(fileName, path, host, documentRegistryBucketKey, scriptSnapshot, scriptVersion, scriptKind, languageVersionOrOptions);
1536                        }
1537                        else {
1538                            // Release old source file and fall through to aquire new file with new script kind
1539                            documentRegistry.releaseDocumentWithKey(oldSourceFile.resolvedPath, documentRegistry.getKeyForCompilationSettings(program.getCompilerOptions()), oldSourceFile.scriptKind, oldSourceFile.impliedNodeFormat);
1540                        }
1541                    }
1542
1543                    // We didn't already have the file.  Fall through and acquire it from the registry.
1544                }
1545
1546                // Could not find this file in the old program, create a new SourceFile for it.
1547                return documentRegistry.acquireDocumentWithKey(fileName, path, host, documentRegistryBucketKey, scriptSnapshot, scriptVersion, scriptKind, languageVersionOrOptions);
1548            }
1549        }
1550
1551        // TODO: GH#18217 frequently asserted as defined
1552        function getProgram(): Program | undefined {
1553            if (languageServiceMode === LanguageServiceMode.Syntactic) {
1554                Debug.assert(program === undefined);
1555                return undefined;
1556            }
1557
1558            synchronizeHostData();
1559
1560            return program;
1561        }
1562
1563        function getBuilderProgram(withLinterProgram?: boolean): BuilderProgram | undefined {
1564            if (languageServiceMode === LanguageServiceMode.Syntactic) {
1565                Debug.assert(builderProgram === undefined);
1566                return undefined;
1567            }
1568
1569            synchronizeHostData(isIncrementalCompilation(host.getCompilationSettings()), withLinterProgram);
1570
1571            return builderProgram;
1572        }
1573
1574        function getAutoImportProvider(): Program | undefined {
1575            return host.getPackageJsonAutoImportProvider?.();
1576        }
1577
1578        function updateIsDefinitionOfReferencedSymbols(referencedSymbols: readonly ReferencedSymbol[], knownSymbolSpans: Set<DocumentSpan>): boolean {
1579            const checker = program.getTypeChecker();
1580            const symbol = getSymbolForProgram();
1581
1582            if (!symbol) return false;
1583
1584            for (const referencedSymbol of referencedSymbols) {
1585                for (const ref of referencedSymbol.references) {
1586                    const refNode = getNodeForSpan(ref);
1587                    Debug.assertIsDefined(refNode);
1588                    if (knownSymbolSpans.has(ref) || FindAllReferences.isDeclarationOfSymbol(refNode, symbol)) {
1589                        knownSymbolSpans.add(ref);
1590                        ref.isDefinition = true;
1591                        const mappedSpan = getMappedDocumentSpan(ref, sourceMapper, maybeBind(host, host.fileExists));
1592                        if (mappedSpan) {
1593                            knownSymbolSpans.add(mappedSpan);
1594                        }
1595                    }
1596                    else {
1597                        ref.isDefinition = false;
1598                    }
1599                }
1600            }
1601
1602            return true;
1603
1604            function getSymbolForProgram(): Symbol | undefined {
1605                for (const referencedSymbol of referencedSymbols) {
1606                    for (const ref of referencedSymbol.references) {
1607                        if (knownSymbolSpans.has(ref)) {
1608                            const refNode = getNodeForSpan(ref);
1609                            Debug.assertIsDefined(refNode);
1610                            return checker.getSymbolAtLocation(refNode);
1611                        }
1612                        const mappedSpan = getMappedDocumentSpan(ref, sourceMapper, maybeBind(host, host.fileExists));
1613                        if (mappedSpan && knownSymbolSpans.has(mappedSpan)) {
1614                            const refNode = getNodeForSpan(mappedSpan);
1615                            if (refNode) {
1616                                return checker.getSymbolAtLocation(refNode);
1617                            }
1618                        }
1619                    }
1620                }
1621
1622                return undefined;
1623            }
1624
1625            function getNodeForSpan(docSpan: DocumentSpan): Node | undefined {
1626                const sourceFile = program.getSourceFile(docSpan.fileName);
1627                if (!sourceFile) return undefined;
1628                const rawNode = getTouchingPropertyName(sourceFile, docSpan.textSpan.start);
1629                const adjustedNode = FindAllReferences.Core.getAdjustedNode(rawNode, { use: FindAllReferences.FindReferencesUse.References });
1630                return adjustedNode;
1631            }
1632        }
1633
1634        function cleanupSemanticCache(): void {
1635            program = undefined!; // TODO: GH#18217
1636        }
1637
1638        function dispose(): void {
1639            if (program) {
1640                // Use paths to ensure we are using correct key and paths as document registry could be created with different current directory than host
1641                const key = documentRegistry.getKeyForCompilationSettings(program.getCompilerOptions());
1642                forEach(program.getSourceFiles(), f =>
1643                    documentRegistry.releaseDocumentWithKey(f.resolvedPath, key, f.scriptKind, f.impliedNodeFormat));
1644                program = undefined!; // TODO: GH#18217
1645            }
1646            host = undefined!;
1647        }
1648
1649        /// Diagnostics
1650        function getSyntacticDiagnostics(fileName: string): DiagnosticWithLocation[] {
1651            synchronizeHostData();
1652
1653            return program.getSyntacticDiagnostics(getValidSourceFile(fileName), cancellationToken).slice();
1654        }
1655
1656        /**
1657         * getSemanticDiagnostics return array of Diagnostics. If '-d' is not enabled, only report semantic errors
1658         * If '-d' enabled, report both semantic and emitter errors
1659         */
1660        function getSemanticDiagnostics(fileName: string): Diagnostic[] {
1661            synchronizeHostData();
1662
1663            const targetSourceFile = getValidSourceFile(fileName);
1664
1665            // Only perform the action per file regardless of '-out' flag as LanguageServiceHost is expected to call this function per file.
1666            // Therefore only get diagnostics for given file.
1667
1668            const semanticDiagnostics = program.getSemanticDiagnostics(targetSourceFile, cancellationToken);
1669            if (!getEmitDeclarations(program.getCompilerOptions())) {
1670                return semanticDiagnostics.slice();
1671            }
1672
1673            // If '-d' is enabled, check for emitter error. One example of emitter error is export class implements non-export interface
1674            const declarationDiagnostics = program.getDeclarationDiagnostics(targetSourceFile, cancellationToken);
1675            return [...semanticDiagnostics, ...declarationDiagnostics];
1676        }
1677
1678        function getSuggestionDiagnostics(fileName: string): DiagnosticWithLocation[] {
1679            synchronizeHostData();
1680            return computeSuggestionDiagnostics(getValidSourceFile(fileName), program, cancellationToken);
1681        }
1682
1683        function getCompilerOptionsDiagnostics() {
1684            synchronizeHostData();
1685            return [...program.getOptionsDiagnostics(cancellationToken), ...program.getGlobalDiagnostics(cancellationToken)];
1686        }
1687
1688        function getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions = emptyOptions, formattingSettings?: FormatCodeSettings): CompletionInfo | undefined {
1689            // Convert from deprecated options names to new names
1690            const fullPreferences: UserPreferences = {
1691                ...identity<UserPreferences>(options), // avoid excess property check
1692                includeCompletionsForModuleExports: options.includeCompletionsForModuleExports || options.includeExternalModuleExports,
1693                includeCompletionsWithInsertText: options.includeCompletionsWithInsertText || options.includeInsertTextCompletions,
1694            };
1695            synchronizeHostData();
1696            return Completions.getCompletionsAtPosition(
1697                host,
1698                program,
1699                log,
1700                getValidSourceFile(fileName),
1701                position,
1702                fullPreferences,
1703                options.triggerCharacter,
1704                options.triggerKind,
1705                cancellationToken,
1706                formattingSettings && formatting.getFormatContext(formattingSettings, host));
1707        }
1708
1709        function getCompletionEntryDetails(fileName: string, position: number, name: string, formattingOptions: FormatCodeSettings | undefined, source: string | undefined, preferences: UserPreferences = emptyOptions, data?: CompletionEntryData): CompletionEntryDetails | undefined {
1710            synchronizeHostData();
1711            return Completions.getCompletionEntryDetails(
1712                program,
1713                log,
1714                getValidSourceFile(fileName),
1715                position,
1716                { name, source, data },
1717                host,
1718                (formattingOptions && formatting.getFormatContext(formattingOptions, host))!, // TODO: GH#18217
1719                preferences,
1720                cancellationToken,
1721            );
1722        }
1723
1724        function getCompletionEntrySymbol(fileName: string, position: number, name: string, source?: string, preferences: UserPreferences = emptyOptions): Symbol | undefined {
1725            synchronizeHostData();
1726            return Completions.getCompletionEntrySymbol(program, log, getValidSourceFile(fileName), position, { name, source }, host, preferences);
1727        }
1728
1729        function getQuickInfoAtPosition(fileName: string, position: number): QuickInfo | undefined {
1730            synchronizeHostData();
1731
1732            const sourceFile = getValidSourceFile(fileName);
1733            const node = getTouchingPropertyName(sourceFile, position);
1734            if (node === sourceFile) {
1735                // Avoid giving quickInfo for the sourceFile as a whole.
1736                return undefined;
1737            }
1738
1739            const typeChecker = program.getTypeChecker();
1740            const nodeForQuickInfo = getNodeForQuickInfo(node);
1741            const symbol = getSymbolAtLocationForQuickInfo(nodeForQuickInfo, typeChecker);
1742
1743            if (!symbol || typeChecker.isUnknownSymbol(symbol)) {
1744                const type = shouldGetType(sourceFile, nodeForQuickInfo, position) ? typeChecker.getTypeAtLocation(nodeForQuickInfo) : undefined;
1745                return type && {
1746                    kind: ScriptElementKind.unknown,
1747                    kindModifiers: ScriptElementKindModifier.none,
1748                    textSpan: createTextSpanFromNode(nodeForQuickInfo, sourceFile),
1749                    displayParts: typeChecker.runWithCancellationToken(cancellationToken, typeChecker => typeToDisplayParts(typeChecker, type, getContainerNode(nodeForQuickInfo))),
1750                    documentation: type.symbol ? type.symbol.getDocumentationComment(typeChecker) : undefined,
1751                    tags: type.symbol ? type.symbol.getJsDocTags(typeChecker) : undefined
1752                };
1753            }
1754
1755            const { symbolKind, displayParts, documentation, tags } = typeChecker.runWithCancellationToken(cancellationToken, typeChecker =>
1756                SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker, symbol, sourceFile, getContainerNode(nodeForQuickInfo), nodeForQuickInfo)
1757            );
1758            return {
1759                kind: symbolKind,
1760                kindModifiers: SymbolDisplay.getSymbolModifiers(typeChecker, symbol),
1761                textSpan: createTextSpanFromNode(nodeForQuickInfo, sourceFile),
1762                displayParts,
1763                documentation,
1764                tags,
1765            };
1766        }
1767
1768        function getNodeForQuickInfo(node: Node): Node {
1769            if (isNewExpression(node.parent) && node.pos === node.parent.pos) {
1770                return node.parent.expression;
1771            }
1772            if (isNamedTupleMember(node.parent) && node.pos === node.parent.pos) {
1773                return node.parent;
1774            }
1775            if (isImportMeta(node.parent) && node.parent.name === node) {
1776                return node.parent;
1777            }
1778            return node;
1779        }
1780
1781        function shouldGetType(sourceFile: SourceFile, node: Node, position: number): boolean {
1782            switch (node.kind) {
1783                case SyntaxKind.Identifier:
1784                    return !isLabelName(node) && !isTagName(node) && !isConstTypeReference(node.parent);
1785                case SyntaxKind.PropertyAccessExpression:
1786                case SyntaxKind.QualifiedName:
1787                    // Don't return quickInfo if inside the comment in `a/**/.b`
1788                    return !isInComment(sourceFile, position);
1789                case SyntaxKind.ThisKeyword:
1790                case SyntaxKind.ThisType:
1791                case SyntaxKind.SuperKeyword:
1792                case SyntaxKind.NamedTupleMember:
1793                    return true;
1794                case SyntaxKind.MetaProperty:
1795                    return isImportMeta(node);
1796                default:
1797                    return false;
1798            }
1799        }
1800
1801        /// Goto definition
1802        function getDefinitionAtPosition(fileName: string, position: number, searchOtherFilesOnly?: boolean, stopAtAlias?: boolean): readonly DefinitionInfo[] | undefined {
1803            synchronizeHostData();
1804            return GoToDefinition.getDefinitionAtPosition(program, getValidSourceFile(fileName), position, searchOtherFilesOnly, stopAtAlias);
1805        }
1806
1807        function getDefinitionAndBoundSpan(fileName: string, position: number): DefinitionInfoAndBoundSpan | undefined {
1808            synchronizeHostData();
1809            return GoToDefinition.getDefinitionAndBoundSpan(program, getValidSourceFile(fileName), position);
1810        }
1811
1812        function getTypeDefinitionAtPosition(fileName: string, position: number): readonly DefinitionInfo[] | undefined {
1813            synchronizeHostData();
1814            return GoToDefinition.getTypeDefinitionAtPosition(program.getTypeChecker(), getValidSourceFile(fileName), position);
1815        }
1816
1817        /// Goto implementation
1818
1819        function getImplementationAtPosition(fileName: string, position: number): ImplementationLocation[] | undefined {
1820            synchronizeHostData();
1821            return FindAllReferences.getImplementationsAtPosition(program, cancellationToken, program.getSourceFiles(), getValidSourceFile(fileName), position);
1822        }
1823
1824        /// References and Occurrences
1825        function getOccurrencesAtPosition(fileName: string, position: number): readonly ReferenceEntry[] | undefined {
1826            return flatMap(
1827                getDocumentHighlights(fileName, position, [fileName]),
1828                entry => entry.highlightSpans.map<ReferenceEntry>(highlightSpan => ({
1829                    fileName: entry.fileName,
1830                    textSpan: highlightSpan.textSpan,
1831                    isWriteAccess: highlightSpan.kind === HighlightSpanKind.writtenReference,
1832                    ...highlightSpan.isInString && { isInString: true },
1833                    ...highlightSpan.contextSpan && { contextSpan: highlightSpan.contextSpan }
1834                }))
1835            );
1836        }
1837
1838        function getDocumentHighlights(fileName: string, position: number, filesToSearch: readonly string[]): DocumentHighlights[] | undefined {
1839            const normalizedFileName = normalizePath(fileName);
1840            Debug.assert(filesToSearch.some(f => normalizePath(f) === normalizedFileName));
1841            synchronizeHostData();
1842            const sourceFilesToSearch = mapDefined(filesToSearch, fileName => program.getSourceFile(fileName));
1843            const sourceFile = getValidSourceFile(fileName);
1844            return DocumentHighlights.getDocumentHighlights(program, cancellationToken, sourceFile, position, sourceFilesToSearch);
1845        }
1846
1847        function findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): RenameLocation[] | undefined {
1848            synchronizeHostData();
1849            const sourceFile = getValidSourceFile(fileName);
1850            const node = getAdjustedRenameLocation(getTouchingPropertyName(sourceFile, position));
1851            if (!Rename.nodeIsEligibleForRename(node)) return undefined;
1852            if (isIdentifier(node) && (isJsxOpeningElement(node.parent) || isJsxClosingElement(node.parent)) && isIntrinsicJsxName(node.escapedText)) {
1853                const { openingElement, closingElement } = node.parent.parent;
1854                return [openingElement, closingElement].map((node): RenameLocation => {
1855                    const textSpan = createTextSpanFromNode(node.tagName, sourceFile);
1856                    return {
1857                        fileName: sourceFile.fileName,
1858                        textSpan,
1859                        ...FindAllReferences.toContextSpan(textSpan, sourceFile, node.parent)
1860                    };
1861                });
1862            }
1863            else {
1864                return getReferencesWorker(node, position, { findInStrings, findInComments, providePrefixAndSuffixTextForRename, use: FindAllReferences.FindReferencesUse.Rename },
1865                    (entry, originalNode, checker) => FindAllReferences.toRenameLocation(entry, originalNode, checker, providePrefixAndSuffixTextForRename || false));
1866            }
1867        }
1868
1869        function getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[] | undefined {
1870            synchronizeHostData();
1871            return getReferencesWorker(getTouchingPropertyName(getValidSourceFile(fileName), position), position, { use: FindAllReferences.FindReferencesUse.References }, FindAllReferences.toReferenceEntry);
1872        }
1873
1874        function getReferencesWorker<T>(node: Node, position: number, options: FindAllReferences.Options, cb: FindAllReferences.ToReferenceOrRenameEntry<T>): T[] | undefined {
1875            synchronizeHostData();
1876
1877            // Exclude default library when renaming as commonly user don't want to change that file.
1878            const sourceFiles = options && options.use === FindAllReferences.FindReferencesUse.Rename
1879                ? program.getSourceFiles().filter(sourceFile => !program.isSourceFileDefaultLibrary(sourceFile))
1880                : program.getSourceFiles();
1881
1882            return FindAllReferences.findReferenceOrRenameEntries(program, cancellationToken, sourceFiles, node, position, options, cb);
1883        }
1884
1885        function findReferences(fileName: string, position: number): ReferencedSymbol[] | undefined {
1886            synchronizeHostData();
1887            return FindAllReferences.findReferencedSymbols(program, cancellationToken, program.getSourceFiles(), getValidSourceFile(fileName), position);
1888        }
1889
1890        function getFileReferences(fileName: string): ReferenceEntry[] {
1891            synchronizeHostData();
1892            return FindAllReferences.Core.getReferencesForFileName(fileName, program, program.getSourceFiles()).map(FindAllReferences.toReferenceEntry);
1893        }
1894
1895        function getNavigateToItems(searchValue: string, maxResultCount?: number, fileName?: string, excludeDtsFiles = false): NavigateToItem[] {
1896            synchronizeHostData();
1897            const sourceFiles = fileName ? [getValidSourceFile(fileName)] : program.getSourceFiles();
1898            return NavigateTo.getNavigateToItems(sourceFiles, program.getTypeChecker(), cancellationToken, searchValue, maxResultCount, excludeDtsFiles);
1899        }
1900
1901        function getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean, forceDtsEmit?: boolean) {
1902            synchronizeHostData();
1903
1904            const sourceFile = getValidSourceFile(fileName);
1905            const customTransformers = host.getCustomTransformers && host.getCustomTransformers();
1906            return getFileEmitOutput(program, sourceFile, !!emitOnlyDtsFiles, cancellationToken, customTransformers, forceDtsEmit);
1907        }
1908
1909        // Signature help
1910        /**
1911         * This is a semantic operation.
1912         */
1913        function getSignatureHelpItems(fileName: string, position: number, { triggerReason }: SignatureHelpItemsOptions = emptyOptions): SignatureHelpItems | undefined {
1914            synchronizeHostData();
1915
1916            const sourceFile = getValidSourceFile(fileName);
1917
1918            return SignatureHelp.getSignatureHelpItems(program, sourceFile, position, triggerReason, cancellationToken);
1919        }
1920
1921        /// Syntactic features
1922        function getNonBoundSourceFile(fileName: string): SourceFile {
1923            return syntaxTreeCache.getCurrentSourceFile(fileName);
1924        }
1925
1926        function getNameOrDottedNameSpan(fileName: string, startPos: number, _endPos: number): TextSpan | undefined {
1927            const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
1928
1929            // Get node at the location
1930            const node = getTouchingPropertyName(sourceFile, startPos);
1931
1932            if (node === sourceFile) {
1933                return undefined;
1934            }
1935
1936            switch (node.kind) {
1937                case SyntaxKind.PropertyAccessExpression:
1938                case SyntaxKind.QualifiedName:
1939                case SyntaxKind.StringLiteral:
1940                case SyntaxKind.FalseKeyword:
1941                case SyntaxKind.TrueKeyword:
1942                case SyntaxKind.NullKeyword:
1943                case SyntaxKind.SuperKeyword:
1944                case SyntaxKind.ThisKeyword:
1945                case SyntaxKind.ThisType:
1946                case SyntaxKind.Identifier:
1947                    break;
1948
1949                // Cant create the text span
1950                default:
1951                    return undefined;
1952            }
1953
1954            let nodeForStartPos = node;
1955            while (true) {
1956                if (isRightSideOfPropertyAccess(nodeForStartPos) || isRightSideOfQualifiedName(nodeForStartPos)) {
1957                    // If on the span is in right side of the the property or qualified name, return the span from the qualified name pos to end of this node
1958                    nodeForStartPos = nodeForStartPos.parent;
1959                }
1960                else if (isNameOfModuleDeclaration(nodeForStartPos)) {
1961                    // If this is name of a module declarations, check if this is right side of dotted module name
1962                    // If parent of the module declaration which is parent of this node is module declaration and its body is the module declaration that this node is name of
1963                    // Then this name is name from dotted module
1964                    if (nodeForStartPos.parent.parent.kind === SyntaxKind.ModuleDeclaration &&
1965                        (nodeForStartPos.parent.parent as ModuleDeclaration).body === nodeForStartPos.parent) {
1966                        // Use parent module declarations name for start pos
1967                        nodeForStartPos = (nodeForStartPos.parent.parent as ModuleDeclaration).name;
1968                    }
1969                    else {
1970                        // We have to use this name for start pos
1971                        break;
1972                    }
1973                }
1974                else {
1975                    // Is not a member expression so we have found the node for start pos
1976                    break;
1977                }
1978            }
1979
1980            return createTextSpanFromBounds(nodeForStartPos.getStart(), node.getEnd());
1981        }
1982
1983        function getBreakpointStatementAtPosition(fileName: string, position: number): TextSpan | undefined {
1984            // doesn't use compiler - no need to synchronize with host
1985            const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
1986
1987            return BreakpointResolver.spanInSourceFileAtLocation(sourceFile, position);
1988        }
1989
1990        function getNavigationBarItems(fileName: string): NavigationBarItem[] {
1991            return NavigationBar.getNavigationBarItems(syntaxTreeCache.getCurrentSourceFile(fileName), cancellationToken);
1992        }
1993
1994        function getNavigationTree(fileName: string): NavigationTree {
1995            return NavigationBar.getNavigationTree(syntaxTreeCache.getCurrentSourceFile(fileName), cancellationToken);
1996        }
1997
1998        function getSemanticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[];
1999        function getSemanticClassifications(fileName: string, span: TextSpan, format?: SemanticClassificationFormat): ClassifiedSpan[] | ClassifiedSpan2020[] {
2000            synchronizeHostData();
2001
2002            const responseFormat = format || SemanticClassificationFormat.Original;
2003            if (responseFormat === SemanticClassificationFormat.TwentyTwenty) {
2004                return classifier.v2020.getSemanticClassifications(program, cancellationToken, getValidSourceFile(fileName), span);
2005            }
2006            else {
2007                return ts.getSemanticClassifications(program.getTypeChecker(), cancellationToken, getValidSourceFile(fileName), program.getClassifiableNames(), span);
2008            }
2009        }
2010
2011        function getEncodedSemanticClassifications(fileName: string, span: TextSpan, format?: SemanticClassificationFormat): Classifications {
2012            synchronizeHostData();
2013
2014            const responseFormat = format || SemanticClassificationFormat.Original;
2015            if (responseFormat === SemanticClassificationFormat.Original) {
2016                return ts.getEncodedSemanticClassifications(program.getTypeChecker(), cancellationToken, getValidSourceFile(fileName), program.getClassifiableNames(), span);
2017            }
2018            else {
2019                return classifier.v2020.getEncodedSemanticClassifications(program, cancellationToken, getValidSourceFile(fileName), span);
2020            }
2021        }
2022
2023        function getSyntacticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[] {
2024            // doesn't use compiler - no need to synchronize with host
2025            return ts.getSyntacticClassifications(cancellationToken, syntaxTreeCache.getCurrentSourceFile(fileName), span);
2026        }
2027
2028        function getEncodedSyntacticClassifications(fileName: string, span: TextSpan): Classifications {
2029            // doesn't use compiler - no need to synchronize with host
2030            return ts.getEncodedSyntacticClassifications(cancellationToken, syntaxTreeCache.getCurrentSourceFile(fileName), span);
2031        }
2032
2033        function getOutliningSpans(fileName: string): OutliningSpan[] {
2034            // doesn't use compiler - no need to synchronize with host
2035            const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
2036            return OutliningElementsCollector.collectElements(sourceFile, cancellationToken);
2037        }
2038
2039        const braceMatching = new Map(getEntries({
2040            [SyntaxKind.OpenBraceToken]: SyntaxKind.CloseBraceToken,
2041            [SyntaxKind.OpenParenToken]: SyntaxKind.CloseParenToken,
2042            [SyntaxKind.OpenBracketToken]: SyntaxKind.CloseBracketToken,
2043            [SyntaxKind.GreaterThanToken]: SyntaxKind.LessThanToken,
2044        }));
2045        braceMatching.forEach((value, key) => braceMatching.set(value.toString(), Number(key) as SyntaxKind));
2046
2047        function getBraceMatchingAtPosition(fileName: string, position: number): TextSpan[] {
2048            const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
2049            const token = getTouchingToken(sourceFile, position);
2050            const matchKind = token.getStart(sourceFile) === position ? braceMatching.get(token.kind.toString()) : undefined;
2051            const match = matchKind && findChildOfKind(token.parent, matchKind, sourceFile);
2052            // We want to order the braces when we return the result.
2053            return match ? [createTextSpanFromNode(token, sourceFile), createTextSpanFromNode(match, sourceFile)].sort((a, b) => a.start - b.start) : emptyArray;
2054        }
2055
2056        function getIndentationAtPosition(fileName: string, position: number, editorOptions: EditorOptions | EditorSettings) {
2057            let start = timestamp();
2058            const settings = toEditorSettings(editorOptions);
2059            const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
2060            log("getIndentationAtPosition: getCurrentSourceFile: " + (timestamp() - start));
2061
2062            start = timestamp();
2063
2064            const result = formatting.SmartIndenter.getIndentation(position, sourceFile, settings);
2065            log("getIndentationAtPosition: computeIndentation  : " + (timestamp() - start));
2066
2067            return result;
2068        }
2069
2070        function getFormattingEditsForRange(fileName: string, start: number, end: number, options: FormatCodeOptions | FormatCodeSettings): TextChange[] {
2071            const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
2072            return formatting.formatSelection(start, end, sourceFile, formatting.getFormatContext(toEditorSettings(options), host));
2073        }
2074
2075        function getFormattingEditsForDocument(fileName: string, options: FormatCodeOptions | FormatCodeSettings): TextChange[] {
2076            return formatting.formatDocument(syntaxTreeCache.getCurrentSourceFile(fileName), formatting.getFormatContext(toEditorSettings(options), host));
2077        }
2078
2079        function getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: FormatCodeOptions | FormatCodeSettings): TextChange[] {
2080            const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
2081            const formatContext = formatting.getFormatContext(toEditorSettings(options), host);
2082
2083            if (!isInComment(sourceFile, position)) {
2084                switch (key) {
2085                    case "{":
2086                        return formatting.formatOnOpeningCurly(position, sourceFile, formatContext);
2087                    case "}":
2088                        return formatting.formatOnClosingCurly(position, sourceFile, formatContext);
2089                    case ";":
2090                        return formatting.formatOnSemicolon(position, sourceFile, formatContext);
2091                    case "\n":
2092                        return formatting.formatOnEnter(position, sourceFile, formatContext);
2093                }
2094            }
2095
2096            return [];
2097        }
2098
2099        function getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: readonly number[], formatOptions: FormatCodeSettings, preferences: UserPreferences = emptyOptions): readonly CodeFixAction[] {
2100            synchronizeHostData();
2101            const sourceFile = getValidSourceFile(fileName);
2102            const span = createTextSpanFromBounds(start, end);
2103            const formatContext = formatting.getFormatContext(formatOptions, host);
2104
2105            return flatMap(deduplicate<number>(errorCodes, equateValues, compareValues), errorCode => {
2106                cancellationToken.throwIfCancellationRequested();
2107                return codefix.getFixes({ errorCode, sourceFile, span, program, host, cancellationToken, formatContext, preferences });
2108            });
2109        }
2110
2111        function getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, preferences: UserPreferences = emptyOptions): CombinedCodeActions {
2112            synchronizeHostData();
2113            Debug.assert(scope.type === "file");
2114            const sourceFile = getValidSourceFile(scope.fileName);
2115            const formatContext = formatting.getFormatContext(formatOptions, host);
2116
2117            return codefix.getAllFixes({ fixId, sourceFile, program, host, cancellationToken, formatContext, preferences });
2118        }
2119
2120        function organizeImports(args: OrganizeImportsArgs, formatOptions: FormatCodeSettings, preferences: UserPreferences = emptyOptions): readonly FileTextChanges[] {
2121            synchronizeHostData();
2122            Debug.assert(args.type === "file");
2123            const sourceFile = getValidSourceFile(args.fileName);
2124            const formatContext = formatting.getFormatContext(formatOptions, host);
2125
2126            const mode = args.mode ?? (args.skipDestructiveCodeActions ? OrganizeImportsMode.SortAndCombine : OrganizeImportsMode.All);
2127            return OrganizeImports.organizeImports(sourceFile, formatContext, host, program, preferences, mode);
2128        }
2129
2130        function getEditsForFileRename(oldFilePath: string, newFilePath: string, formatOptions: FormatCodeSettings, preferences: UserPreferences = emptyOptions): readonly FileTextChanges[] {
2131            return ts.getEditsForFileRename(getProgram()!, oldFilePath, newFilePath, host, formatting.getFormatContext(formatOptions, host), preferences, sourceMapper);
2132        }
2133
2134        function applyCodeActionCommand(action: CodeActionCommand, formatSettings?: FormatCodeSettings): Promise<ApplyCodeActionCommandResult>;
2135        function applyCodeActionCommand(action: CodeActionCommand[], formatSettings?: FormatCodeSettings): Promise<ApplyCodeActionCommandResult[]>;
2136        function applyCodeActionCommand(action: CodeActionCommand | CodeActionCommand[], formatSettings?: FormatCodeSettings): Promise<ApplyCodeActionCommandResult | ApplyCodeActionCommandResult[]>;
2137        function applyCodeActionCommand(fileName: Path, action: CodeActionCommand): Promise<ApplyCodeActionCommandResult>;
2138        function applyCodeActionCommand(fileName: Path, action: CodeActionCommand[]): Promise<ApplyCodeActionCommandResult[]>;
2139        function applyCodeActionCommand(fileName: Path | CodeActionCommand | CodeActionCommand[], actionOrFormatSettingsOrUndefined?: CodeActionCommand | CodeActionCommand[] | FormatCodeSettings): Promise<ApplyCodeActionCommandResult | ApplyCodeActionCommandResult[]> {
2140            const action = typeof fileName === "string" ? actionOrFormatSettingsOrUndefined as CodeActionCommand | CodeActionCommand[] : fileName as CodeActionCommand[];
2141            return isArray(action) ? Promise.all(action.map(a => applySingleCodeActionCommand(a))) : applySingleCodeActionCommand(action);
2142        }
2143
2144        function applySingleCodeActionCommand(action: CodeActionCommand): Promise<ApplyCodeActionCommandResult> {
2145            const getPath = (path: string): Path => toPath(path, currentDirectory, getCanonicalFileName);
2146            Debug.assertEqual(action.type, "install package");
2147            return host.installPackage
2148                ? host.installPackage({ fileName: getPath(action.file), packageName: action.packageName })
2149                : Promise.reject("Host does not implement `installPackage`");
2150        }
2151
2152        function getDocCommentTemplateAtPosition(fileName: string, position: number, options?: DocCommentTemplateOptions): TextInsertion | undefined {
2153            return JsDoc.getDocCommentTemplateAtPosition(getNewLineOrDefaultFromHost(host), syntaxTreeCache.getCurrentSourceFile(fileName), position, options);
2154        }
2155
2156        function isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): boolean {
2157            // '<' is currently not supported, figuring out if we're in a Generic Type vs. a comparison is too
2158            // expensive to do during typing scenarios
2159            // i.e. whether we're dealing with:
2160            //      var x = new foo<| ( with class foo<T>{} )
2161            // or
2162            //      var y = 3 <|
2163            if (openingBrace === CharacterCodes.lessThan) {
2164                return false;
2165            }
2166
2167            const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
2168
2169            // Check if in a context where we don't want to perform any insertion
2170            if (isInString(sourceFile, position)) {
2171                return false;
2172            }
2173
2174            if (isInsideJsxElementOrAttribute(sourceFile, position)) {
2175                return openingBrace === CharacterCodes.openBrace;
2176            }
2177
2178            if (isInTemplateString(sourceFile, position)) {
2179                return false;
2180            }
2181
2182            switch (openingBrace) {
2183                case CharacterCodes.singleQuote:
2184                case CharacterCodes.doubleQuote:
2185                case CharacterCodes.backtick:
2186                    return !isInComment(sourceFile, position);
2187            }
2188
2189            return true;
2190        }
2191
2192        function getJsxClosingTagAtPosition(fileName: string, position: number): JsxClosingTagInfo | undefined {
2193            const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
2194            const token = findPrecedingToken(position, sourceFile);
2195            if (!token) return undefined;
2196            const element = token.kind === SyntaxKind.GreaterThanToken && isJsxOpeningElement(token.parent) ? token.parent.parent
2197                : isJsxText(token) && isJsxElement(token.parent) ? token.parent : undefined;
2198            if (element && isUnclosedTag(element)) {
2199                return { newText: `</${element.openingElement.tagName.getText(sourceFile)}>` };
2200            }
2201            const fragment = token.kind === SyntaxKind.GreaterThanToken && isJsxOpeningFragment(token.parent) ? token.parent.parent
2202                : isJsxText(token) && isJsxFragment(token.parent) ? token.parent : undefined;
2203            if (fragment && isUnclosedFragment(fragment)) {
2204                return { newText: "</>" };
2205            }
2206        }
2207
2208        function getLinesForRange(sourceFile: SourceFile, textRange: TextRange) {
2209            return {
2210                lineStarts: sourceFile.getLineStarts(),
2211                firstLine: sourceFile.getLineAndCharacterOfPosition(textRange.pos).line,
2212                lastLine: sourceFile.getLineAndCharacterOfPosition(textRange.end).line
2213            };
2214        }
2215
2216        function toggleLineComment(fileName: string, textRange: TextRange, insertComment?: boolean): TextChange[] {
2217            const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
2218            const textChanges: TextChange[] = [];
2219            const { lineStarts, firstLine, lastLine } = getLinesForRange(sourceFile, textRange);
2220
2221            let isCommenting = insertComment || false;
2222            let leftMostPosition = Number.MAX_VALUE;
2223            const lineTextStarts = new Map<string, number>();
2224            const firstNonWhitespaceCharacterRegex = new RegExp(/\S/);
2225            const isJsx = isInsideJsxElement(sourceFile, lineStarts[firstLine]);
2226            const openComment = isJsx ? "{/*" : "//";
2227
2228            // Check each line before any text changes.
2229            for (let i = firstLine; i <= lastLine; i++) {
2230                const lineText = sourceFile.text.substring(lineStarts[i], sourceFile.getLineEndOfPosition(lineStarts[i]));
2231
2232                // Find the start of text and the left-most character. No-op on empty lines.
2233                const regExec = firstNonWhitespaceCharacterRegex.exec(lineText);
2234                if (regExec) {
2235                    leftMostPosition = Math.min(leftMostPosition, regExec.index);
2236                    lineTextStarts.set(i.toString(), regExec.index);
2237
2238                    if (lineText.substr(regExec.index, openComment.length) !== openComment) {
2239                        isCommenting = insertComment === undefined || insertComment;
2240                    }
2241                }
2242            }
2243
2244            // Push all text changes.
2245            for (let i = firstLine; i <= lastLine; i++) {
2246                // If the range is multiline and ends on a beginning of a line, don't comment/uncomment.
2247                if (firstLine !== lastLine && lineStarts[i] === textRange.end) {
2248                    continue;
2249                }
2250
2251                const lineTextStart = lineTextStarts.get(i.toString());
2252
2253                // If the line is not an empty line; otherwise no-op.
2254                if (lineTextStart !== undefined) {
2255                    if (isJsx) {
2256                        textChanges.push.apply(textChanges, toggleMultilineComment(fileName, { pos: lineStarts[i] + leftMostPosition, end: sourceFile.getLineEndOfPosition(lineStarts[i]) }, isCommenting, isJsx));
2257                    }
2258                    else if (isCommenting) {
2259                        textChanges.push({
2260                            newText: openComment,
2261                            span: {
2262                                length: 0,
2263                                start: lineStarts[i] + leftMostPosition
2264                            }
2265                        });
2266                    }
2267                    else if (sourceFile.text.substr(lineStarts[i] + lineTextStart, openComment.length) === openComment) {
2268                        textChanges.push({
2269                            newText: "",
2270                            span: {
2271                                length: openComment.length,
2272                                start: lineStarts[i] + lineTextStart
2273                            }
2274                        });
2275                    }
2276                }
2277            }
2278
2279            return textChanges;
2280        }
2281
2282        function toggleMultilineComment(fileName: string, textRange: TextRange, insertComment?: boolean, isInsideJsx?: boolean): TextChange[] {
2283            const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
2284            const textChanges: TextChange[] = [];
2285            const { text } = sourceFile;
2286
2287            let hasComment = false;
2288            let isCommenting = insertComment || false;
2289            const positions = [] as number[] as SortedArray<number>;
2290
2291            let { pos } = textRange;
2292            const isJsx = isInsideJsx !== undefined ? isInsideJsx : isInsideJsxElement(sourceFile, pos);
2293
2294            const openMultiline = isJsx ? "{/*" : "/*";
2295            const closeMultiline = isJsx ? "*/}" : "*/";
2296            const openMultilineRegex = isJsx ? "\\{\\/\\*" : "\\/\\*";
2297            const closeMultilineRegex = isJsx ? "\\*\\/\\}" : "\\*\\/";
2298
2299            // Get all comment positions
2300            while (pos <= textRange.end) {
2301                // Start of comment is considered inside comment.
2302                const offset = text.substr(pos, openMultiline.length) === openMultiline ? openMultiline.length : 0;
2303                const commentRange = isInComment(sourceFile, pos + offset);
2304
2305                // If position is in a comment add it to the positions array.
2306                if (commentRange) {
2307                    // Comment range doesn't include the brace character. Increase it to include them.
2308                    if (isJsx) {
2309                        commentRange.pos--;
2310                        commentRange.end++;
2311                    }
2312
2313                    positions.push(commentRange.pos);
2314                    if (commentRange.kind === SyntaxKind.MultiLineCommentTrivia) {
2315                        positions.push(commentRange.end);
2316                    }
2317
2318                    hasComment = true;
2319                    pos = commentRange.end + 1;
2320                }
2321                else { // If it's not in a comment range, then we need to comment the uncommented portions.
2322                    const newPos = text.substring(pos, textRange.end).search(`(${openMultilineRegex})|(${closeMultilineRegex})`);
2323
2324                    isCommenting = insertComment !== undefined
2325                        ? insertComment
2326                        : isCommenting || !isTextWhiteSpaceLike(text, pos, newPos === -1 ? textRange.end : pos + newPos); // If isCommenting is already true we don't need to check whitespace again.
2327                    pos = newPos === -1 ? textRange.end + 1 : pos + newPos + closeMultiline.length;
2328                }
2329            }
2330
2331            // If it didn't found a comment and isCommenting is false means is only empty space.
2332            // We want to insert comment in this scenario.
2333            if (isCommenting || !hasComment) {
2334                if (isInComment(sourceFile, textRange.pos)?.kind !== SyntaxKind.SingleLineCommentTrivia) {
2335                    insertSorted(positions, textRange.pos, compareValues);
2336                }
2337                insertSorted(positions, textRange.end, compareValues);
2338
2339                // Insert open comment if the first position is not a comment already.
2340                const firstPos = positions[0];
2341                if (text.substr(firstPos, openMultiline.length) !== openMultiline) {
2342                    textChanges.push({
2343                        newText: openMultiline,
2344                        span: {
2345                            length: 0,
2346                            start: firstPos
2347                        }
2348                    });
2349                }
2350
2351                // Insert open and close comment to all positions between first and last. Exclusive.
2352                for (let i = 1; i < positions.length - 1; i++) {
2353                    if (text.substr(positions[i] - closeMultiline.length, closeMultiline.length) !== closeMultiline) {
2354                        textChanges.push({
2355                            newText: closeMultiline,
2356                            span: {
2357                                length: 0,
2358                                start: positions[i]
2359                            }
2360                        });
2361                    }
2362
2363                    if (text.substr(positions[i], openMultiline.length) !== openMultiline) {
2364                        textChanges.push({
2365                            newText: openMultiline,
2366                            span: {
2367                                length: 0,
2368                                start: positions[i]
2369                            }
2370                        });
2371                    }
2372                }
2373
2374                // Insert open comment if the last position is not a comment already.
2375                if (textChanges.length % 2 !== 0) {
2376                    textChanges.push({
2377                        newText: closeMultiline,
2378                        span: {
2379                            length: 0,
2380                            start: positions[positions.length - 1]
2381                        }
2382                    });
2383                }
2384            }
2385            else {
2386                // If is not commenting then remove all comments found.
2387                for (const pos of positions) {
2388                    const from = pos - closeMultiline.length > 0 ? pos - closeMultiline.length : 0;
2389                    const offset = text.substr(from, closeMultiline.length) === closeMultiline ? closeMultiline.length : 0;
2390                    textChanges.push({
2391                        newText: "",
2392                        span: {
2393                            length: openMultiline.length,
2394                            start: pos - offset
2395                        }
2396                    });
2397                }
2398            }
2399
2400            return textChanges;
2401        }
2402
2403        function commentSelection(fileName: string, textRange: TextRange): TextChange[] {
2404            const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
2405            const { firstLine, lastLine } = getLinesForRange(sourceFile, textRange);
2406
2407            // If there is a selection that is on the same line, add multiline.
2408            return firstLine === lastLine && textRange.pos !== textRange.end
2409                ? toggleMultilineComment(fileName, textRange, /*insertComment*/ true)
2410                : toggleLineComment(fileName, textRange, /*insertComment*/ true);
2411        }
2412
2413        function uncommentSelection(fileName: string, textRange: TextRange): TextChange[] {
2414            const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
2415            const textChanges: TextChange[] = [];
2416            const { pos } = textRange;
2417            let { end } = textRange;
2418
2419            // If cursor is not a selection we need to increase the end position
2420            // to include the start of the comment.
2421            if (pos === end) {
2422                end += isInsideJsxElement(sourceFile, pos) ? 2 : 1;
2423            }
2424
2425            for (let i = pos; i <= end; i++) {
2426                const commentRange = isInComment(sourceFile, i);
2427                if (commentRange) {
2428                    switch (commentRange.kind) {
2429                        case SyntaxKind.SingleLineCommentTrivia:
2430                            textChanges.push.apply(textChanges, toggleLineComment(fileName, { end: commentRange.end, pos: commentRange.pos + 1 }, /*insertComment*/ false));
2431                            break;
2432                        case SyntaxKind.MultiLineCommentTrivia:
2433                            textChanges.push.apply(textChanges, toggleMultilineComment(fileName, { end: commentRange.end, pos: commentRange.pos + 1 }, /*insertComment*/ false));
2434                    }
2435
2436                    i = commentRange.end + 1;
2437                }
2438            }
2439
2440            return textChanges;
2441        }
2442
2443        function isUnclosedTag({ openingElement, closingElement, parent }: JsxElement): boolean {
2444            return !tagNamesAreEquivalent(openingElement.tagName, closingElement.tagName) ||
2445                isJsxElement(parent) && tagNamesAreEquivalent(openingElement.tagName, parent.openingElement.tagName) && isUnclosedTag(parent);
2446        }
2447
2448        function isUnclosedFragment({ closingFragment, parent }: JsxFragment): boolean {
2449            return !!(closingFragment.flags & NodeFlags.ThisNodeHasError) || (isJsxFragment(parent) && isUnclosedFragment(parent));
2450        }
2451
2452        function getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan | undefined {
2453            const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
2454            const range = formatting.getRangeOfEnclosingComment(sourceFile, position);
2455            return range && (!onlyMultiLine || range.kind === SyntaxKind.MultiLineCommentTrivia) ? createTextSpanFromRange(range) : undefined;
2456        }
2457
2458        function getTodoComments(fileName: string, descriptors: TodoCommentDescriptor[]): TodoComment[] {
2459            // Note: while getting todo comments seems like a syntactic operation, we actually
2460            // treat it as a semantic operation here.  This is because we expect our host to call
2461            // this on every single file.  If we treat this syntactically, then that will cause
2462            // us to populate and throw away the tree in our syntax tree cache for each file.  By
2463            // treating this as a semantic operation, we can access any tree without throwing
2464            // anything away.
2465            synchronizeHostData();
2466
2467            const sourceFile = getValidSourceFile(fileName);
2468
2469            cancellationToken.throwIfCancellationRequested();
2470
2471            const fileContents = sourceFile.text;
2472            const result: TodoComment[] = [];
2473
2474            // Exclude node_modules or oh_modules files as we don't want to show the todos of external libraries.
2475            if (descriptors.length > 0 && !isNodeModulesFile(sourceFile.fileName) && !isOHModulesFile(sourceFile.fileName)) {
2476                const regExp = getTodoCommentsRegExp();
2477
2478                let matchArray: RegExpExecArray | null;
2479                while (matchArray = regExp.exec(fileContents)) {
2480                    cancellationToken.throwIfCancellationRequested();
2481
2482                    // If we got a match, here is what the match array will look like.  Say the source text is:
2483                    //
2484                    //      "    // hack   1"
2485                    //
2486                    // The result array with the regexp:    will be:
2487                    //
2488                    //      ["// hack   1", "// ", "hack   1", undefined, "hack"]
2489                    //
2490                    // Here are the relevant capture groups:
2491                    //  0) The full match for the entire regexp.
2492                    //  1) The preamble to the message portion.
2493                    //  2) The message portion.
2494                    //  3...N) The descriptor that was matched - by index.  'undefined' for each
2495                    //         descriptor that didn't match.  an actual value if it did match.
2496                    //
2497                    //  i.e. 'undefined' in position 3 above means TODO(jason) didn't match.
2498                    //       "hack"      in position 4 means HACK did match.
2499                    const firstDescriptorCaptureIndex = 3;
2500                    Debug.assert(matchArray.length === descriptors.length + firstDescriptorCaptureIndex);
2501
2502                    const preamble = matchArray[1];
2503                    const matchPosition = matchArray.index + preamble.length;
2504
2505                    // OK, we have found a match in the file.  This is only an acceptable match if
2506                    // it is contained within a comment.
2507                    if (!isInComment(sourceFile, matchPosition)) {
2508                        continue;
2509                    }
2510
2511                    let descriptor: TodoCommentDescriptor | undefined;
2512                    for (let i = 0; i < descriptors.length; i++) {
2513                        if (matchArray[i + firstDescriptorCaptureIndex]) {
2514                            descriptor = descriptors[i];
2515                        }
2516                    }
2517                    if (descriptor === undefined) return Debug.fail();
2518
2519                    // We don't want to match something like 'TODOBY', so we make sure a non
2520                    // letter/digit follows the match.
2521                    if (isLetterOrDigit(fileContents.charCodeAt(matchPosition + descriptor.text.length))) {
2522                        continue;
2523                    }
2524
2525                    const message = matchArray[2];
2526                    result.push({ descriptor, message, position: matchPosition });
2527                }
2528            }
2529
2530            return result;
2531
2532            function escapeRegExp(str: string): string {
2533                return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
2534            }
2535
2536            function getTodoCommentsRegExp(): RegExp {
2537                // NOTE: `?:` means 'non-capture group'.  It allows us to have groups without having to
2538                // filter them out later in the final result array.
2539
2540                // TODO comments can appear in one of the following forms:
2541                //
2542                //  1)      // TODO     or  /////////// TODO
2543                //
2544                //  2)      /* TODO     or  /********** TODO
2545                //
2546                //  3)      /*
2547                //           *   TODO
2548                //           */
2549                //
2550                // The following three regexps are used to match the start of the text up to the TODO
2551                // comment portion.
2552                const singleLineCommentStart = /(?:\/\/+\s*)/.source;
2553                const multiLineCommentStart = /(?:\/\*+\s*)/.source;
2554                const anyNumberOfSpacesAndAsterisksAtStartOfLine = /(?:^(?:\s|\*)*)/.source;
2555
2556                // Match any of the above three TODO comment start regexps.
2557                // Note that the outermost group *is* a capture group.  We want to capture the preamble
2558                // so that we can determine the starting position of the TODO comment match.
2559                const preamble = "(" + anyNumberOfSpacesAndAsterisksAtStartOfLine + "|" + singleLineCommentStart + "|" + multiLineCommentStart + ")";
2560
2561                // Takes the descriptors and forms a regexp that matches them as if they were literals.
2562                // For example, if the descriptors are "TODO(jason)" and "HACK", then this will be:
2563                //
2564                //      (?:(TODO\(jason\))|(HACK))
2565                //
2566                // Note that the outermost group is *not* a capture group, but the innermost groups
2567                // *are* capture groups.  By capturing the inner literals we can determine after
2568                // matching which descriptor we are dealing with.
2569                const literals = "(?:" + map(descriptors, d => "(" + escapeRegExp(d.text) + ")").join("|") + ")";
2570
2571                // After matching a descriptor literal, the following regexp matches the rest of the
2572                // text up to the end of the line (or */).
2573                const endOfLineOrEndOfComment = /(?:$|\*\/)/.source;
2574                const messageRemainder = /(?:.*?)/.source;
2575
2576                // This is the portion of the match we'll return as part of the TODO comment result. We
2577                // match the literal portion up to the end of the line or end of comment.
2578                const messagePortion = "(" + literals + messageRemainder + ")";
2579                const regExpString = preamble + messagePortion + endOfLineOrEndOfComment;
2580
2581                // The final regexp will look like this:
2582                // /((?:\/\/+\s*)|(?:\/\*+\s*)|(?:^(?:\s|\*)*))((?:(TODO\(jason\))|(HACK))(?:.*?))(?:$|\*\/)/gim
2583
2584                // The flags of the regexp are important here.
2585                //  'g' is so that we are doing a global search and can find matches several times
2586                //  in the input.
2587                //
2588                //  'i' is for case insensitivity (We do this to match C# TODO comment code).
2589                //
2590                //  'm' is so we can find matches in a multi-line input.
2591                return new RegExp(regExpString, "gim");
2592            }
2593
2594            function isLetterOrDigit(char: number): boolean {
2595                return (char >= CharacterCodes.a && char <= CharacterCodes.z) ||
2596                    (char >= CharacterCodes.A && char <= CharacterCodes.Z) ||
2597                    (char >= CharacterCodes._0 && char <= CharacterCodes._9);
2598            }
2599
2600            function isNodeModulesFile(path: string): boolean {
2601                return stringContains(path, "/node_modules/");
2602            }
2603
2604            function isOHModulesFile(path: string): boolean {
2605                return stringContains(path, "/oh_modules/");
2606            }
2607        }
2608
2609        function getRenameInfo(fileName: string, position: number, preferences: UserPreferences | RenameInfoOptions | undefined): RenameInfo {
2610            synchronizeHostData();
2611            return Rename.getRenameInfo(program, getValidSourceFile(fileName), position, preferences || {});
2612        }
2613
2614        function getRefactorContext(file: SourceFile, positionOrRange: number | TextRange, preferences: UserPreferences, formatOptions?: FormatCodeSettings, triggerReason?: RefactorTriggerReason, kind?: string): RefactorContext {
2615            const [startPosition, endPosition] = typeof positionOrRange === "number" ? [positionOrRange, undefined] : [positionOrRange.pos, positionOrRange.end];
2616            return {
2617                file,
2618                startPosition,
2619                endPosition,
2620                program: getProgram()!,
2621                host,
2622                formatContext: formatting.getFormatContext(formatOptions!, host), // TODO: GH#18217
2623                cancellationToken,
2624                preferences,
2625                triggerReason,
2626                kind
2627            };
2628        }
2629
2630        function getInlayHintsContext(file: SourceFile, span: TextSpan, preferences: UserPreferences): InlayHintsContext {
2631            return {
2632                file,
2633                program: getProgram()!,
2634                host,
2635                span,
2636                preferences,
2637                cancellationToken,
2638            };
2639        }
2640
2641        function getSmartSelectionRange(fileName: string, position: number): SelectionRange {
2642            return SmartSelectionRange.getSmartSelectionRange(position, syntaxTreeCache.getCurrentSourceFile(fileName));
2643        }
2644
2645        function getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences = emptyOptions, triggerReason: RefactorTriggerReason, kind: string): ApplicableRefactorInfo[] {
2646            synchronizeHostData();
2647            const file = getValidSourceFile(fileName);
2648            return refactor.getApplicableRefactors(getRefactorContext(file, positionOrRange, preferences, emptyOptions, triggerReason, kind));
2649        }
2650
2651        function getEditsForRefactor(
2652            fileName: string,
2653            formatOptions: FormatCodeSettings,
2654            positionOrRange: number | TextRange,
2655            refactorName: string,
2656            actionName: string,
2657            preferences: UserPreferences = emptyOptions,
2658        ): RefactorEditInfo | undefined {
2659            synchronizeHostData();
2660            const file = getValidSourceFile(fileName);
2661            return refactor.getEditsForRefactor(getRefactorContext(file, positionOrRange, preferences, formatOptions), refactorName, actionName);
2662        }
2663
2664        function toLineColumnOffset(fileName: string, position: number): LineAndCharacter {
2665            // Go to Definition supports returning a zero-length span at position 0 for
2666            // non-existent files. We need to special-case the conversion of position 0
2667            // to avoid a crash trying to get the text for that file, since this function
2668            // otherwise assumes that 'fileName' is the name of a file that exists.
2669            if (position === 0) {
2670                return { line: 0, character: 0 };
2671            }
2672            return sourceMapper.toLineColumnOffset(fileName, position);
2673        }
2674
2675        function prepareCallHierarchy(fileName: string, position: number): CallHierarchyItem | CallHierarchyItem[] | undefined {
2676            synchronizeHostData();
2677            const declarations = CallHierarchy.resolveCallHierarchyDeclaration(program, getTouchingPropertyName(getValidSourceFile(fileName), position));
2678            return declarations && mapOneOrMany(declarations, declaration => CallHierarchy.createCallHierarchyItem(program, declaration));
2679        }
2680
2681        function provideCallHierarchyIncomingCalls(fileName: string, position: number): CallHierarchyIncomingCall[] {
2682            synchronizeHostData();
2683            const sourceFile = getValidSourceFile(fileName);
2684            const declaration = firstOrOnly(CallHierarchy.resolveCallHierarchyDeclaration(program, position === 0 ? sourceFile : getTouchingPropertyName(sourceFile, position)));
2685            return declaration ? CallHierarchy.getIncomingCalls(program, declaration, cancellationToken) : [];
2686        }
2687
2688        function provideCallHierarchyOutgoingCalls(fileName: string, position: number): CallHierarchyOutgoingCall[] {
2689            synchronizeHostData();
2690            const sourceFile = getValidSourceFile(fileName);
2691            const declaration = firstOrOnly(CallHierarchy.resolveCallHierarchyDeclaration(program, position === 0 ? sourceFile : getTouchingPropertyName(sourceFile, position)));
2692            return declaration ? CallHierarchy.getOutgoingCalls(program, declaration) : [];
2693        }
2694
2695        function provideInlayHints(fileName: string, span: TextSpan, preferences: UserPreferences = emptyOptions): InlayHint[] {
2696            synchronizeHostData();
2697            const sourceFile = getValidSourceFile(fileName);
2698            return InlayHints.provideInlayHints(getInlayHintsContext(sourceFile, span, preferences));
2699        }
2700
2701        function updateRootFiles(rootFiles: string[]) {
2702            host.getScriptFileNames = () => rootFiles
2703        }
2704
2705        function getProps(): string[] {
2706            return host.uiProps ? host.uiProps : [];
2707        }
2708
2709        const ls: LanguageService = {
2710            dispose,
2711            cleanupSemanticCache,
2712            getSyntacticDiagnostics,
2713            getSemanticDiagnostics,
2714            getSuggestionDiagnostics,
2715            getCompilerOptionsDiagnostics,
2716            getSyntacticClassifications,
2717            getSemanticClassifications,
2718            getEncodedSyntacticClassifications,
2719            getEncodedSemanticClassifications,
2720            getCompletionsAtPosition,
2721            getCompletionEntryDetails,
2722            getCompletionEntrySymbol,
2723            getSignatureHelpItems,
2724            getQuickInfoAtPosition,
2725            getDefinitionAtPosition,
2726            getDefinitionAndBoundSpan,
2727            getImplementationAtPosition,
2728            getTypeDefinitionAtPosition,
2729            getReferencesAtPosition,
2730            findReferences,
2731            getFileReferences,
2732            getOccurrencesAtPosition,
2733            getDocumentHighlights,
2734            getNameOrDottedNameSpan,
2735            getBreakpointStatementAtPosition,
2736            getNavigateToItems,
2737            getRenameInfo,
2738            getSmartSelectionRange,
2739            findRenameLocations,
2740            getNavigationBarItems,
2741            getNavigationTree,
2742            getOutliningSpans,
2743            getTodoComments,
2744            getBraceMatchingAtPosition,
2745            getIndentationAtPosition,
2746            getFormattingEditsForRange,
2747            getFormattingEditsForDocument,
2748            getFormattingEditsAfterKeystroke,
2749            getDocCommentTemplateAtPosition,
2750            isValidBraceCompletionAtPosition,
2751            getJsxClosingTagAtPosition,
2752            getSpanOfEnclosingComment,
2753            getCodeFixesAtPosition,
2754            getCombinedCodeFix,
2755            applyCodeActionCommand,
2756            organizeImports,
2757            getEditsForFileRename,
2758            getEmitOutput,
2759            getNonBoundSourceFile,
2760            getProgram,
2761            getBuilderProgram,
2762            getCurrentProgram: () => program,
2763            getAutoImportProvider,
2764            updateIsDefinitionOfReferencedSymbols,
2765            getApplicableRefactors,
2766            getEditsForRefactor,
2767            toLineColumnOffset,
2768            getSourceMapper: () => sourceMapper,
2769            clearSourceMapperCache: () => sourceMapper.clearCache(),
2770            prepareCallHierarchy,
2771            provideCallHierarchyIncomingCalls,
2772            provideCallHierarchyOutgoingCalls,
2773            toggleLineComment,
2774            toggleMultilineComment,
2775            commentSelection,
2776            uncommentSelection,
2777            provideInlayHints,
2778            updateRootFiles,
2779            getProps
2780        };
2781
2782        switch (languageServiceMode) {
2783            case LanguageServiceMode.Semantic:
2784                break;
2785            case LanguageServiceMode.PartialSemantic:
2786                invalidOperationsInPartialSemanticMode.forEach(key =>
2787                    ls[key] = () => {
2788                        throw new Error(`LanguageService Operation: ${key} not allowed in LanguageServiceMode.PartialSemantic`);
2789                    }
2790                );
2791                break;
2792            case LanguageServiceMode.Syntactic:
2793                invalidOperationsInSyntacticMode.forEach(key =>
2794                    ls[key] = () => {
2795                        throw new Error(`LanguageService Operation: ${key} not allowed in LanguageServiceMode.Syntactic`);
2796                    }
2797                );
2798                break;
2799            default:
2800                Debug.assertNever(languageServiceMode);
2801        }
2802        return ls;
2803    }
2804
2805    /* @internal */
2806    /** Names in the name table are escaped, so an identifier `__foo` will have a name table entry `___foo`. */
2807    export function getNameTable(sourceFile: SourceFile): UnderscoreEscapedMap<number> {
2808        if (!sourceFile.nameTable) {
2809            initializeNameTable(sourceFile);
2810        }
2811
2812        return sourceFile.nameTable!; // TODO: GH#18217
2813    }
2814
2815    function initializeNameTable(sourceFile: SourceFile): void {
2816        const nameTable = sourceFile.nameTable = new Map();
2817        sourceFile.forEachChild(function walk(node) {
2818            if (isIdentifier(node) && !isTagName(node) && node.escapedText || isStringOrNumericLiteralLike(node) && literalIsName(node)) {
2819                const text = getEscapedTextOfIdentifierOrLiteral(node);
2820                nameTable.set(text, nameTable.get(text) === undefined ? node.pos : -1);
2821            }
2822            else if (isPrivateIdentifier(node)) {
2823                const text = node.escapedText;
2824                nameTable.set(text, nameTable.get(text) === undefined ? node.pos : -1);
2825            }
2826
2827            forEachChild(node, walk);
2828            if (hasJSDocNodes(node)) {
2829                for (const jsDoc of node.jsDoc!) {
2830                    forEachChild(jsDoc, walk);
2831                }
2832            }
2833        });
2834    }
2835
2836    /**
2837     * We want to store any numbers/strings if they were a name that could be
2838     * related to a declaration.  So, if we have 'import x = require("something")'
2839     * then we want 'something' to be in the name table.  Similarly, if we have
2840     * "a['propname']" then we want to store "propname" in the name table.
2841     */
2842    function literalIsName(node: StringLiteralLike | NumericLiteral): boolean {
2843        return isDeclarationName(node) ||
2844            node.parent.kind === SyntaxKind.ExternalModuleReference ||
2845            isArgumentOfElementAccessExpression(node) ||
2846            isLiteralComputedPropertyDeclarationName(node);
2847    }
2848
2849    /**
2850     * Returns the containing object literal property declaration given a possible name node, e.g. "a" in x = { "a": 1 }
2851     */
2852    /* @internal */
2853    export function getContainingObjectLiteralElement(node: Node): ObjectLiteralElementWithName | undefined {
2854        const element = getContainingObjectLiteralElementWorker(node);
2855        return element && (isObjectLiteralExpression(element.parent) || isJsxAttributes(element.parent)) ? element as ObjectLiteralElementWithName : undefined;
2856    }
2857    function getContainingObjectLiteralElementWorker(node: Node): ObjectLiteralElement | undefined {
2858        switch (node.kind) {
2859            case SyntaxKind.StringLiteral:
2860            case SyntaxKind.NoSubstitutionTemplateLiteral:
2861            case SyntaxKind.NumericLiteral:
2862                if (node.parent.kind === SyntaxKind.ComputedPropertyName) {
2863                    return isObjectLiteralElement(node.parent.parent) ? node.parent.parent : undefined;
2864                }
2865            // falls through
2866
2867            case SyntaxKind.Identifier:
2868                return isObjectLiteralElement(node.parent) &&
2869                    (node.parent.parent.kind === SyntaxKind.ObjectLiteralExpression || node.parent.parent.kind === SyntaxKind.JsxAttributes) &&
2870                    node.parent.name === node ? node.parent : undefined;
2871        }
2872        return undefined;
2873    }
2874
2875    /* @internal */
2876    export type ObjectLiteralElementWithName = ObjectLiteralElement & { name: PropertyName; parent: ObjectLiteralExpression | JsxAttributes };
2877
2878    function getSymbolAtLocationForQuickInfo(node: Node, checker: TypeChecker): Symbol | undefined {
2879        const object = getContainingObjectLiteralElement(node);
2880        if (object) {
2881            const contextualType = checker.getContextualType(object.parent);
2882            const properties = contextualType && getPropertySymbolsFromContextualType(object, checker, contextualType, /*unionSymbolOk*/ false);
2883            if (properties && properties.length === 1) {
2884                return first(properties);
2885            }
2886        }
2887        return checker.getSymbolAtLocation(node);
2888    }
2889
2890    /** Gets all symbols for one property. Does not get symbols for every property. */
2891    /* @internal */
2892    export function getPropertySymbolsFromContextualType(node: ObjectLiteralElementWithName, checker: TypeChecker, contextualType: Type, unionSymbolOk: boolean): readonly Symbol[] {
2893        const name = getNameFromPropertyName(node.name);
2894        if (!name) return emptyArray;
2895        if (!contextualType.isUnion()) {
2896            const symbol = contextualType.getProperty(name);
2897            return symbol ? [symbol] : emptyArray;
2898        }
2899
2900        const discriminatedPropertySymbols = mapDefined(contextualType.types, t => (isObjectLiteralExpression(node.parent)|| isJsxAttributes(node.parent)) && checker.isTypeInvalidDueToUnionDiscriminant(t, node.parent) ? undefined : t.getProperty(name));
2901        if (unionSymbolOk && (discriminatedPropertySymbols.length === 0 || discriminatedPropertySymbols.length === contextualType.types.length)) {
2902            const symbol = contextualType.getProperty(name);
2903            if (symbol) return [symbol];
2904        }
2905        if (discriminatedPropertySymbols.length === 0) {
2906            // Bad discriminant -- do again without discriminating
2907            return mapDefined(contextualType.types, t => t.getProperty(name));
2908        }
2909        return discriminatedPropertySymbols;
2910    }
2911
2912    function isArgumentOfElementAccessExpression(node: Node) {
2913        return node &&
2914            node.parent &&
2915            node.parent.kind === SyntaxKind.ElementAccessExpression &&
2916            (node.parent as ElementAccessExpression).argumentExpression === node;
2917    }
2918
2919    /// getDefaultLibraryFilePath
2920    declare const __dirname: string;
2921
2922    /**
2923     * Get the path of the default library files (lib.d.ts) as distributed with the typescript
2924     * node package.
2925     * The functionality is not supported if the ts module is consumed outside of a node module.
2926     */
2927    export function getDefaultLibFilePath(options: CompilerOptions): string {
2928        // Check __dirname is defined and that we are on a node.js system.
2929        if (typeof __dirname !== "undefined") {
2930            return combinePaths(__dirname, getDefaultLibFileName(options));
2931        }
2932
2933        throw new Error("getDefaultLibFilePath is only supported when consumed as a node module. ");
2934    }
2935
2936    setObjectAllocator(getServicesObjectAllocator());
2937}
2938