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