• 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                // There was originally an assert here to ensure that getOrCreateSourceFileByPath was called in typical CompilerHost lifetime.
1489                // We removed this assert because currently this function may also be called outside of the typical CompilerHost lifetime,
1490                // in which case the compilerhost may be undefined.
1491                // The program is asking for this file, check first if the host can locate it.
1492                // If the host can not locate the file, then it does not exist. return undefined
1493                // to the program to allow reporting of errors for missing files.
1494                const scriptSnapshot = host.getScriptSnapshot(fileName);
1495                if (!scriptSnapshot) {
1496                    return undefined;
1497                }
1498
1499                const scriptKind = getScriptKind(fileName, host);
1500                const scriptVersion = host.getScriptVersion(fileName);
1501
1502                // Check if the language version has changed since we last created a program; if they are the same,
1503                // it is safe to reuse the sourceFiles; if not, then the shape of the AST can change, and the oldSourceFile
1504                // can not be reused. we have to dump all syntax trees and create new ones.
1505                if (!shouldCreateNewSourceFile) {
1506                    // Check if the old program had this file already
1507                    const oldSourceFile = program && program.getSourceFileByPath(path);
1508                    if (oldSourceFile) {
1509                        // We already had a source file for this file name.  Go to the registry to
1510                        // ensure that we get the right up to date version of it.  We need this to
1511                        // address the following race-condition.  Specifically, say we have the following:
1512                        //
1513                        //      LS1
1514                        //          \
1515                        //           DocumentRegistry
1516                        //          /
1517                        //      LS2
1518                        //
1519                        // Each LS has a reference to file 'foo.ts' at version 1.  LS2 then updates
1520                        // it's version of 'foo.ts' to version 2.  This will cause LS2 and the
1521                        // DocumentRegistry to have version 2 of the document.  However, LS1 will
1522                        // have version 1.  And *importantly* this source file will be *corrupt*.
1523                        // The act of creating version 2 of the file irrevocably damages the version
1524                        // 1 file.
1525                        //
1526                        // So, later when we call into LS1, we need to make sure that it doesn't use
1527                        // it's source file any more, and instead defers to DocumentRegistry to get
1528                        // either version 1, version 2 (or some other version) depending on what the
1529                        // host says should be used.
1530
1531                        // We do not support the scenario where a host can modify a registered
1532                        // file's script kind, i.e. in one project some file is treated as ".ts"
1533                        // and in another as ".js"
1534                        if (scriptKind === oldSourceFile.scriptKind) {
1535                            return documentRegistry.updateDocumentWithKey(fileName, path, host, documentRegistryBucketKey, scriptSnapshot, scriptVersion, scriptKind, languageVersionOrOptions);
1536                        }
1537                        else {
1538                            // Release old source file and fall through to aquire new file with new script kind
1539                            documentRegistry.releaseDocumentWithKey(oldSourceFile.resolvedPath, documentRegistry.getKeyForCompilationSettings(program.getCompilerOptions()), oldSourceFile.scriptKind, oldSourceFile.impliedNodeFormat);
1540                        }
1541                    }
1542
1543                    // We didn't already have the file.  Fall through and acquire it from the registry.
1544                }
1545
1546                // Could not find this file in the old program, create a new SourceFile for it.
1547                return documentRegistry.acquireDocumentWithKey(fileName, path, host, documentRegistryBucketKey, scriptSnapshot, scriptVersion, scriptKind, languageVersionOrOptions);
1548            }
1549        }
1550
1551        // TODO: GH#18217 frequently asserted as defined
1552        function getProgram(): Program | undefined {
1553            if (languageServiceMode === LanguageServiceMode.Syntactic) {
1554                Debug.assert(program === undefined);
1555                return undefined;
1556            }
1557
1558            synchronizeHostData();
1559
1560            return program;
1561        }
1562
1563        function getBuilderProgram(withLinterProgram?: boolean): BuilderProgram | undefined {
1564            if (languageServiceMode === LanguageServiceMode.Syntactic) {
1565                Debug.assert(builderProgram === undefined);
1566                return undefined;
1567            }
1568
1569            synchronizeHostData(isIncrementalCompilation(host.getCompilationSettings()), withLinterProgram);
1570
1571            return builderProgram;
1572        }
1573
1574        function getAutoImportProvider(): Program | undefined {
1575            return host.getPackageJsonAutoImportProvider?.();
1576        }
1577
1578        function updateIsDefinitionOfReferencedSymbols(referencedSymbols: readonly ReferencedSymbol[], knownSymbolSpans: Set<DocumentSpan>): boolean {
1579            const checker = program.getTypeChecker();
1580            const symbol = getSymbolForProgram();
1581
1582            if (!symbol) return false;
1583
1584            for (const referencedSymbol of referencedSymbols) {
1585                for (const ref of referencedSymbol.references) {
1586                    const refNode = getNodeForSpan(ref);
1587                    Debug.assertIsDefined(refNode);
1588                    if (knownSymbolSpans.has(ref) || FindAllReferences.isDeclarationOfSymbol(refNode, symbol)) {
1589                        knownSymbolSpans.add(ref);
1590                        ref.isDefinition = true;
1591                        const mappedSpan = getMappedDocumentSpan(ref, sourceMapper, maybeBind(host, host.fileExists));
1592                        if (mappedSpan) {
1593                            knownSymbolSpans.add(mappedSpan);
1594                        }
1595                    }
1596                    else {
1597                        ref.isDefinition = false;
1598                    }
1599                }
1600            }
1601
1602            return true;
1603
1604            function getSymbolForProgram(): Symbol | undefined {
1605                for (const referencedSymbol of referencedSymbols) {
1606                    for (const ref of referencedSymbol.references) {
1607                        if (knownSymbolSpans.has(ref)) {
1608                            const refNode = getNodeForSpan(ref);
1609                            Debug.assertIsDefined(refNode);
1610                            return checker.getSymbolAtLocation(refNode);
1611                        }
1612                        const mappedSpan = getMappedDocumentSpan(ref, sourceMapper, maybeBind(host, host.fileExists));
1613                        if (mappedSpan && knownSymbolSpans.has(mappedSpan)) {
1614                            const refNode = getNodeForSpan(mappedSpan);
1615                            if (refNode) {
1616                                return checker.getSymbolAtLocation(refNode);
1617                            }
1618                        }
1619                    }
1620                }
1621
1622                return undefined;
1623            }
1624
1625            function getNodeForSpan(docSpan: DocumentSpan): Node | undefined {
1626                const sourceFile = program.getSourceFile(docSpan.fileName);
1627                if (!sourceFile) return undefined;
1628                const rawNode = getTouchingPropertyName(sourceFile, docSpan.textSpan.start);
1629                const adjustedNode = FindAllReferences.Core.getAdjustedNode(rawNode, { use: FindAllReferences.FindReferencesUse.References });
1630                return adjustedNode;
1631            }
1632        }
1633
1634        function cleanupSemanticCache(): void {
1635            program = undefined!; // TODO: GH#18217
1636        }
1637
1638        function dispose(): void {
1639            if (program) {
1640                // Use paths to ensure we are using correct key and paths as document registry could be created with different current directory than host
1641                const key = documentRegistry.getKeyForCompilationSettings(program.getCompilerOptions());
1642                forEach(program.getSourceFiles(), f =>
1643                    documentRegistry.releaseDocumentWithKey(f.resolvedPath, key, f.scriptKind, f.impliedNodeFormat));
1644                program = undefined!; // TODO: GH#18217
1645            }
1646            host = undefined!;
1647        }
1648
1649        /// Diagnostics
1650        function getSyntacticDiagnostics(fileName: string): DiagnosticWithLocation[] {
1651            synchronizeHostData();
1652
1653            return program.getSyntacticDiagnostics(getValidSourceFile(fileName), cancellationToken).slice();
1654        }
1655
1656        /**
1657         * getSemanticDiagnostics return array of Diagnostics. If '-d' is not enabled, only report semantic errors
1658         * If '-d' enabled, report both semantic and emitter errors
1659         */
1660        function getSemanticDiagnostics(fileName: string): Diagnostic[] {
1661            synchronizeHostData();
1662
1663            const targetSourceFile = getValidSourceFile(fileName);
1664
1665            // Only perform the action per file regardless of '-out' flag as LanguageServiceHost is expected to call this function per file.
1666            // Therefore only get diagnostics for given file.
1667
1668            const semanticDiagnostics = program.getSemanticDiagnostics(targetSourceFile, cancellationToken);
1669            if (!getEmitDeclarations(program.getCompilerOptions())) {
1670                return semanticDiagnostics.slice();
1671            }
1672
1673            // If '-d' is enabled, check for emitter error. One example of emitter error is export class implements non-export interface
1674            const declarationDiagnostics = program.getDeclarationDiagnostics(targetSourceFile, cancellationToken);
1675            return [...semanticDiagnostics, ...declarationDiagnostics];
1676        }
1677
1678        function getSuggestionDiagnostics(fileName: string): DiagnosticWithLocation[] {
1679            synchronizeHostData();
1680            return computeSuggestionDiagnostics(getValidSourceFile(fileName), program, cancellationToken);
1681        }
1682
1683        function getCompilerOptionsDiagnostics() {
1684            synchronizeHostData();
1685            return [...program.getOptionsDiagnostics(cancellationToken), ...program.getGlobalDiagnostics(cancellationToken)];
1686        }
1687
1688        function getCompletionsAtPosition(fileName: string, position: number, options: GetCompletionsAtPositionOptions = emptyOptions, formattingSettings?: FormatCodeSettings): CompletionInfo | undefined {
1689            // Convert from deprecated options names to new names
1690            const fullPreferences: UserPreferences = {
1691                ...identity<UserPreferences>(options), // avoid excess property check
1692                includeCompletionsForModuleExports: options.includeCompletionsForModuleExports || options.includeExternalModuleExports,
1693                includeCompletionsWithInsertText: options.includeCompletionsWithInsertText || options.includeInsertTextCompletions,
1694            };
1695            synchronizeHostData();
1696            return Completions.getCompletionsAtPosition(
1697                host,
1698                program,
1699                log,
1700                getValidSourceFile(fileName),
1701                position,
1702                fullPreferences,
1703                options.triggerCharacter,
1704                options.triggerKind,
1705                cancellationToken,
1706                formattingSettings && formatting.getFormatContext(formattingSettings, host));
1707        }
1708
1709        function getCompletionEntryDetails(fileName: string, position: number, name: string, formattingOptions: FormatCodeSettings | undefined, source: string | undefined, preferences: UserPreferences = emptyOptions, data?: CompletionEntryData): CompletionEntryDetails | undefined {
1710            synchronizeHostData();
1711            return Completions.getCompletionEntryDetails(
1712                program,
1713                log,
1714                getValidSourceFile(fileName),
1715                position,
1716                { name, source, data },
1717                host,
1718                (formattingOptions && formatting.getFormatContext(formattingOptions, host))!, // TODO: GH#18217
1719                preferences,
1720                cancellationToken,
1721            );
1722        }
1723
1724        function getCompletionEntrySymbol(fileName: string, position: number, name: string, source?: string, preferences: UserPreferences = emptyOptions): Symbol | undefined {
1725            synchronizeHostData();
1726            return Completions.getCompletionEntrySymbol(program, log, getValidSourceFile(fileName), position, { name, source }, host, preferences);
1727        }
1728
1729        function getQuickInfoAtPosition(fileName: string, position: number): QuickInfo | undefined {
1730            synchronizeHostData();
1731
1732            const sourceFile = getValidSourceFile(fileName);
1733            const node = getTouchingPropertyName(sourceFile, position);
1734            if (node === sourceFile) {
1735                // Avoid giving quickInfo for the sourceFile as a whole.
1736                return undefined;
1737            }
1738
1739            const typeChecker = program.getTypeChecker();
1740            const nodeForQuickInfo = getNodeForQuickInfo(node);
1741            const symbol = getSymbolAtLocationForQuickInfo(nodeForQuickInfo, typeChecker);
1742
1743            if (!symbol || typeChecker.isUnknownSymbol(symbol)) {
1744                const type = shouldGetType(sourceFile, nodeForQuickInfo, position) ? typeChecker.getTypeAtLocation(nodeForQuickInfo) : undefined;
1745                return type && {
1746                    kind: ScriptElementKind.unknown,
1747                    kindModifiers: ScriptElementKindModifier.none,
1748                    textSpan: createTextSpanFromNode(nodeForQuickInfo, sourceFile),
1749                    displayParts: typeChecker.runWithCancellationToken(cancellationToken, typeChecker => typeToDisplayParts(typeChecker, type, getContainerNode(nodeForQuickInfo))),
1750                    documentation: type.symbol ? type.symbol.getDocumentationComment(typeChecker) : undefined,
1751                    tags: type.symbol ? type.symbol.getJsDocTags(typeChecker) : undefined
1752                };
1753            }
1754
1755            const { symbolKind, displayParts, documentation, tags } = typeChecker.runWithCancellationToken(cancellationToken, typeChecker =>
1756                SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker, symbol, sourceFile, getContainerNode(nodeForQuickInfo), nodeForQuickInfo)
1757            );
1758            return {
1759                kind: symbolKind,
1760                kindModifiers: SymbolDisplay.getSymbolModifiers(typeChecker, symbol),
1761                textSpan: createTextSpanFromNode(nodeForQuickInfo, sourceFile),
1762                displayParts,
1763                documentation,
1764                tags,
1765            };
1766        }
1767
1768        function getNodeForQuickInfo(node: Node): Node {
1769            if (isNewExpression(node.parent) && node.pos === node.parent.pos) {
1770                return node.parent.expression;
1771            }
1772            if (isNamedTupleMember(node.parent) && node.pos === node.parent.pos) {
1773                return node.parent;
1774            }
1775            if (isImportMeta(node.parent) && node.parent.name === node) {
1776                return node.parent;
1777            }
1778            return node;
1779        }
1780
1781        function shouldGetType(sourceFile: SourceFile, node: Node, position: number): boolean {
1782            switch (node.kind) {
1783                case SyntaxKind.Identifier:
1784                    return !isLabelName(node) && !isTagName(node) && !isConstTypeReference(node.parent);
1785                case SyntaxKind.PropertyAccessExpression:
1786                case SyntaxKind.QualifiedName:
1787                    // Don't return quickInfo if inside the comment in `a/**/.b`
1788                    return !isInComment(sourceFile, position);
1789                case SyntaxKind.ThisKeyword:
1790                case SyntaxKind.ThisType:
1791                case SyntaxKind.SuperKeyword:
1792                case SyntaxKind.NamedTupleMember:
1793                    return true;
1794                case SyntaxKind.MetaProperty:
1795                    return isImportMeta(node);
1796                default:
1797                    return false;
1798            }
1799        }
1800
1801        /// Goto definition
1802        function getDefinitionAtPosition(fileName: string, position: number, searchOtherFilesOnly?: boolean, stopAtAlias?: boolean): readonly DefinitionInfo[] | undefined {
1803            synchronizeHostData();
1804            return GoToDefinition.getDefinitionAtPosition(program, getValidSourceFile(fileName), position, searchOtherFilesOnly, stopAtAlias);
1805        }
1806
1807        function getDefinitionAndBoundSpan(fileName: string, position: number): DefinitionInfoAndBoundSpan | undefined {
1808            synchronizeHostData();
1809            return GoToDefinition.getDefinitionAndBoundSpan(program, getValidSourceFile(fileName), position);
1810        }
1811
1812        function getTypeDefinitionAtPosition(fileName: string, position: number): readonly DefinitionInfo[] | undefined {
1813            synchronizeHostData();
1814            return GoToDefinition.getTypeDefinitionAtPosition(program.getTypeChecker(), getValidSourceFile(fileName), position);
1815        }
1816
1817        /// Goto implementation
1818
1819        function getImplementationAtPosition(fileName: string, position: number): ImplementationLocation[] | undefined {
1820            synchronizeHostData();
1821            return FindAllReferences.getImplementationsAtPosition(program, cancellationToken, program.getSourceFiles(), getValidSourceFile(fileName), position);
1822        }
1823
1824        /// References and Occurrences
1825        function getOccurrencesAtPosition(fileName: string, position: number): readonly ReferenceEntry[] | undefined {
1826            return flatMap(
1827                getDocumentHighlights(fileName, position, [fileName]),
1828                entry => entry.highlightSpans.map<ReferenceEntry>(highlightSpan => ({
1829                    fileName: entry.fileName,
1830                    textSpan: highlightSpan.textSpan,
1831                    isWriteAccess: highlightSpan.kind === HighlightSpanKind.writtenReference,
1832                    ...highlightSpan.isInString && { isInString: true },
1833                    ...highlightSpan.contextSpan && { contextSpan: highlightSpan.contextSpan }
1834                }))
1835            );
1836        }
1837
1838        function getDocumentHighlights(fileName: string, position: number, filesToSearch: readonly string[]): DocumentHighlights[] | undefined {
1839            const normalizedFileName = normalizePath(fileName);
1840            Debug.assert(filesToSearch.some(f => normalizePath(f) === normalizedFileName));
1841            synchronizeHostData();
1842            const sourceFilesToSearch = mapDefined(filesToSearch, fileName => program.getSourceFile(fileName));
1843            const sourceFile = getValidSourceFile(fileName);
1844            return DocumentHighlights.getDocumentHighlights(program, cancellationToken, sourceFile, position, sourceFilesToSearch);
1845        }
1846
1847        function findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): RenameLocation[] | undefined {
1848            synchronizeHostData();
1849            const sourceFile = getValidSourceFile(fileName);
1850            const node = getAdjustedRenameLocation(getTouchingPropertyName(sourceFile, position));
1851            if (!Rename.nodeIsEligibleForRename(node)) return undefined;
1852            if (isIdentifier(node) && (isJsxOpeningElement(node.parent) || isJsxClosingElement(node.parent)) && isIntrinsicJsxName(node.escapedText)) {
1853                const { openingElement, closingElement } = node.parent.parent;
1854                return [openingElement, closingElement].map((node): RenameLocation => {
1855                    const textSpan = createTextSpanFromNode(node.tagName, sourceFile);
1856                    return {
1857                        fileName: sourceFile.fileName,
1858                        textSpan,
1859                        ...FindAllReferences.toContextSpan(textSpan, sourceFile, node.parent)
1860                    };
1861                });
1862            }
1863            else {
1864                return getReferencesWorker(node, position, { findInStrings, findInComments, providePrefixAndSuffixTextForRename, use: FindAllReferences.FindReferencesUse.Rename },
1865                    (entry, originalNode, checker) => FindAllReferences.toRenameLocation(entry, originalNode, checker, providePrefixAndSuffixTextForRename || false));
1866            }
1867        }
1868
1869        function getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[] | undefined {
1870            synchronizeHostData();
1871            return getReferencesWorker(getTouchingPropertyName(getValidSourceFile(fileName), position), position, { use: FindAllReferences.FindReferencesUse.References }, FindAllReferences.toReferenceEntry);
1872        }
1873
1874        function getReferencesWorker<T>(node: Node, position: number, options: FindAllReferences.Options, cb: FindAllReferences.ToReferenceOrRenameEntry<T>): T[] | undefined {
1875            synchronizeHostData();
1876
1877            // Exclude default library when renaming as commonly user don't want to change that file.
1878            const sourceFiles = options && options.use === FindAllReferences.FindReferencesUse.Rename
1879                ? program.getSourceFiles().filter(sourceFile => !program.isSourceFileDefaultLibrary(sourceFile))
1880                : program.getSourceFiles();
1881
1882            return FindAllReferences.findReferenceOrRenameEntries(program, cancellationToken, sourceFiles, node, position, options, cb);
1883        }
1884
1885        function findReferences(fileName: string, position: number): ReferencedSymbol[] | undefined {
1886            synchronizeHostData();
1887            return FindAllReferences.findReferencedSymbols(program, cancellationToken, program.getSourceFiles(), getValidSourceFile(fileName), position);
1888        }
1889
1890        function getFileReferences(fileName: string): ReferenceEntry[] {
1891            synchronizeHostData();
1892            return FindAllReferences.Core.getReferencesForFileName(fileName, program, program.getSourceFiles()).map(FindAllReferences.toReferenceEntry);
1893        }
1894
1895        function getNavigateToItems(searchValue: string, maxResultCount?: number, fileName?: string, excludeDtsFiles = false): NavigateToItem[] {
1896            synchronizeHostData();
1897            const sourceFiles = fileName ? [getValidSourceFile(fileName)] : program.getSourceFiles();
1898            return NavigateTo.getNavigateToItems(sourceFiles, program.getTypeChecker(), cancellationToken, searchValue, maxResultCount, excludeDtsFiles);
1899        }
1900
1901        function getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean, forceDtsEmit?: boolean) {
1902            synchronizeHostData();
1903
1904            const sourceFile = getValidSourceFile(fileName);
1905            const customTransformers = host.getCustomTransformers && host.getCustomTransformers();
1906            return getFileEmitOutput(program, sourceFile, !!emitOnlyDtsFiles, cancellationToken, customTransformers, forceDtsEmit);
1907        }
1908
1909        // Signature help
1910        /**
1911         * This is a semantic operation.
1912         */
1913        function getSignatureHelpItems(fileName: string, position: number, { triggerReason }: SignatureHelpItemsOptions = emptyOptions): SignatureHelpItems | undefined {
1914            synchronizeHostData();
1915
1916            const sourceFile = getValidSourceFile(fileName);
1917
1918            return SignatureHelp.getSignatureHelpItems(program, sourceFile, position, triggerReason, cancellationToken);
1919        }
1920
1921        /// Syntactic features
1922        function getNonBoundSourceFile(fileName: string): SourceFile {
1923            return syntaxTreeCache.getCurrentSourceFile(fileName);
1924        }
1925
1926        function getNameOrDottedNameSpan(fileName: string, startPos: number, _endPos: number): TextSpan | undefined {
1927            const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
1928
1929            // Get node at the location
1930            const node = getTouchingPropertyName(sourceFile, startPos);
1931
1932            if (node === sourceFile) {
1933                return undefined;
1934            }
1935
1936            switch (node.kind) {
1937                case SyntaxKind.PropertyAccessExpression:
1938                case SyntaxKind.QualifiedName:
1939                case SyntaxKind.StringLiteral:
1940                case SyntaxKind.FalseKeyword:
1941                case SyntaxKind.TrueKeyword:
1942                case SyntaxKind.NullKeyword:
1943                case SyntaxKind.SuperKeyword:
1944                case SyntaxKind.ThisKeyword:
1945                case SyntaxKind.ThisType:
1946                case SyntaxKind.Identifier:
1947                    break;
1948
1949                // Cant create the text span
1950                default:
1951                    return undefined;
1952            }
1953
1954            let nodeForStartPos = node;
1955            while (true) {
1956                if (isRightSideOfPropertyAccess(nodeForStartPos) || isRightSideOfQualifiedName(nodeForStartPos)) {
1957                    // If on the span is in right side of the the property or qualified name, return the span from the qualified name pos to end of this node
1958                    nodeForStartPos = nodeForStartPos.parent;
1959                }
1960                else if (isNameOfModuleDeclaration(nodeForStartPos)) {
1961                    // If this is name of a module declarations, check if this is right side of dotted module name
1962                    // If parent of the module declaration which is parent of this node is module declaration and its body is the module declaration that this node is name of
1963                    // Then this name is name from dotted module
1964                    if (nodeForStartPos.parent.parent.kind === SyntaxKind.ModuleDeclaration &&
1965                        (nodeForStartPos.parent.parent as ModuleDeclaration).body === nodeForStartPos.parent) {
1966                        // Use parent module declarations name for start pos
1967                        nodeForStartPos = (nodeForStartPos.parent.parent as ModuleDeclaration).name;
1968                    }
1969                    else {
1970                        // We have to use this name for start pos
1971                        break;
1972                    }
1973                }
1974                else {
1975                    // Is not a member expression so we have found the node for start pos
1976                    break;
1977                }
1978            }
1979
1980            return createTextSpanFromBounds(nodeForStartPos.getStart(), node.getEnd());
1981        }
1982
1983        function getBreakpointStatementAtPosition(fileName: string, position: number): TextSpan | undefined {
1984            // doesn't use compiler - no need to synchronize with host
1985            const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
1986
1987            return BreakpointResolver.spanInSourceFileAtLocation(sourceFile, position);
1988        }
1989
1990        function getNavigationBarItems(fileName: string): NavigationBarItem[] {
1991            return NavigationBar.getNavigationBarItems(syntaxTreeCache.getCurrentSourceFile(fileName), cancellationToken);
1992        }
1993
1994        function getNavigationTree(fileName: string): NavigationTree {
1995            return NavigationBar.getNavigationTree(syntaxTreeCache.getCurrentSourceFile(fileName), cancellationToken);
1996        }
1997
1998        function getSemanticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[];
1999        function getSemanticClassifications(fileName: string, span: TextSpan, format?: SemanticClassificationFormat): ClassifiedSpan[] | ClassifiedSpan2020[] {
2000            synchronizeHostData();
2001
2002            const responseFormat = format || SemanticClassificationFormat.Original;
2003            if (responseFormat === SemanticClassificationFormat.TwentyTwenty) {
2004                return classifier.v2020.getSemanticClassifications(program, cancellationToken, getValidSourceFile(fileName), span);
2005            }
2006            else {
2007                return ts.getSemanticClassifications(program.getTypeChecker(), cancellationToken, getValidSourceFile(fileName), program.getClassifiableNames(), span);
2008            }
2009        }
2010
2011        function getEncodedSemanticClassifications(fileName: string, span: TextSpan, format?: SemanticClassificationFormat): Classifications {
2012            synchronizeHostData();
2013
2014            const responseFormat = format || SemanticClassificationFormat.Original;
2015            if (responseFormat === SemanticClassificationFormat.Original) {
2016                return ts.getEncodedSemanticClassifications(program.getTypeChecker(), cancellationToken, getValidSourceFile(fileName), program.getClassifiableNames(), span);
2017            }
2018            else {
2019                return classifier.v2020.getEncodedSemanticClassifications(program, cancellationToken, getValidSourceFile(fileName), span);
2020            }
2021        }
2022
2023        function getSyntacticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[] {
2024            // doesn't use compiler - no need to synchronize with host
2025            return ts.getSyntacticClassifications(cancellationToken, syntaxTreeCache.getCurrentSourceFile(fileName), span);
2026        }
2027
2028        function getEncodedSyntacticClassifications(fileName: string, span: TextSpan): Classifications {
2029            // doesn't use compiler - no need to synchronize with host
2030            return ts.getEncodedSyntacticClassifications(cancellationToken, syntaxTreeCache.getCurrentSourceFile(fileName), span);
2031        }
2032
2033        function getOutliningSpans(fileName: string): OutliningSpan[] {
2034            // doesn't use compiler - no need to synchronize with host
2035            const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
2036            return OutliningElementsCollector.collectElements(sourceFile, cancellationToken);
2037        }
2038
2039        const braceMatching = new Map(getEntries({
2040            [SyntaxKind.OpenBraceToken]: SyntaxKind.CloseBraceToken,
2041            [SyntaxKind.OpenParenToken]: SyntaxKind.CloseParenToken,
2042            [SyntaxKind.OpenBracketToken]: SyntaxKind.CloseBracketToken,
2043            [SyntaxKind.GreaterThanToken]: SyntaxKind.LessThanToken,
2044        }));
2045        braceMatching.forEach((value, key) => braceMatching.set(value.toString(), Number(key) as SyntaxKind));
2046
2047        function getBraceMatchingAtPosition(fileName: string, position: number): TextSpan[] {
2048            const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
2049            const token = getTouchingToken(sourceFile, position);
2050            const matchKind = token.getStart(sourceFile) === position ? braceMatching.get(token.kind.toString()) : undefined;
2051            const match = matchKind && findChildOfKind(token.parent, matchKind, sourceFile);
2052            // We want to order the braces when we return the result.
2053            return match ? [createTextSpanFromNode(token, sourceFile), createTextSpanFromNode(match, sourceFile)].sort((a, b) => a.start - b.start) : emptyArray;
2054        }
2055
2056        function getIndentationAtPosition(fileName: string, position: number, editorOptions: EditorOptions | EditorSettings) {
2057            let start = timestamp();
2058            const settings = toEditorSettings(editorOptions);
2059            const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
2060            log("getIndentationAtPosition: getCurrentSourceFile: " + (timestamp() - start));
2061
2062            start = timestamp();
2063
2064            const result = formatting.SmartIndenter.getIndentation(position, sourceFile, settings);
2065            log("getIndentationAtPosition: computeIndentation  : " + (timestamp() - start));
2066
2067            return result;
2068        }
2069
2070        function getFormattingEditsForRange(fileName: string, start: number, end: number, options: FormatCodeOptions | FormatCodeSettings): TextChange[] {
2071            const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
2072            return formatting.formatSelection(start, end, sourceFile, formatting.getFormatContext(toEditorSettings(options), host));
2073        }
2074
2075        function getFormattingEditsForDocument(fileName: string, options: FormatCodeOptions | FormatCodeSettings): TextChange[] {
2076            return formatting.formatDocument(syntaxTreeCache.getCurrentSourceFile(fileName), formatting.getFormatContext(toEditorSettings(options), host));
2077        }
2078
2079        function getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: FormatCodeOptions | FormatCodeSettings): TextChange[] {
2080            const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
2081            const formatContext = formatting.getFormatContext(toEditorSettings(options), host);
2082
2083            if (!isInComment(sourceFile, position)) {
2084                switch (key) {
2085                    case "{":
2086                        return formatting.formatOnOpeningCurly(position, sourceFile, formatContext);
2087                    case "}":
2088                        return formatting.formatOnClosingCurly(position, sourceFile, formatContext);
2089                    case ";":
2090                        return formatting.formatOnSemicolon(position, sourceFile, formatContext);
2091                    case "\n":
2092                        return formatting.formatOnEnter(position, sourceFile, formatContext);
2093                }
2094            }
2095
2096            return [];
2097        }
2098
2099        function getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: readonly number[], formatOptions: FormatCodeSettings, preferences: UserPreferences = emptyOptions): readonly CodeFixAction[] {
2100            synchronizeHostData();
2101            const sourceFile = getValidSourceFile(fileName);
2102            const span = createTextSpanFromBounds(start, end);
2103            const formatContext = formatting.getFormatContext(formatOptions, host);
2104
2105            return flatMap(deduplicate<number>(errorCodes, equateValues, compareValues), errorCode => {
2106                cancellationToken.throwIfCancellationRequested();
2107                return codefix.getFixes({ errorCode, sourceFile, span, program, host, cancellationToken, formatContext, preferences });
2108            });
2109        }
2110
2111        function getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, preferences: UserPreferences = emptyOptions): CombinedCodeActions {
2112            synchronizeHostData();
2113            Debug.assert(scope.type === "file");
2114            const sourceFile = getValidSourceFile(scope.fileName);
2115            const formatContext = formatting.getFormatContext(formatOptions, host);
2116
2117            return codefix.getAllFixes({ fixId, sourceFile, program, host, cancellationToken, formatContext, preferences });
2118        }
2119
2120        function organizeImports(args: OrganizeImportsArgs, formatOptions: FormatCodeSettings, preferences: UserPreferences = emptyOptions): readonly FileTextChanges[] {
2121            synchronizeHostData();
2122            Debug.assert(args.type === "file");
2123            const sourceFile = getValidSourceFile(args.fileName);
2124            const formatContext = formatting.getFormatContext(formatOptions, host);
2125
2126            const mode = args.mode ?? (args.skipDestructiveCodeActions ? OrganizeImportsMode.SortAndCombine : OrganizeImportsMode.All);
2127            return OrganizeImports.organizeImports(sourceFile, formatContext, host, program, preferences, mode);
2128        }
2129
2130        function getEditsForFileRename(oldFilePath: string, newFilePath: string, formatOptions: FormatCodeSettings, preferences: UserPreferences = emptyOptions): readonly FileTextChanges[] {
2131            return ts.getEditsForFileRename(getProgram()!, oldFilePath, newFilePath, host, formatting.getFormatContext(formatOptions, host), preferences, sourceMapper);
2132        }
2133
2134        function applyCodeActionCommand(action: CodeActionCommand, formatSettings?: FormatCodeSettings): Promise<ApplyCodeActionCommandResult>;
2135        function applyCodeActionCommand(action: CodeActionCommand[], formatSettings?: FormatCodeSettings): Promise<ApplyCodeActionCommandResult[]>;
2136        function applyCodeActionCommand(action: CodeActionCommand | CodeActionCommand[], formatSettings?: FormatCodeSettings): Promise<ApplyCodeActionCommandResult | ApplyCodeActionCommandResult[]>;
2137        function applyCodeActionCommand(fileName: Path, action: CodeActionCommand): Promise<ApplyCodeActionCommandResult>;
2138        function applyCodeActionCommand(fileName: Path, action: CodeActionCommand[]): Promise<ApplyCodeActionCommandResult[]>;
2139        function applyCodeActionCommand(fileName: Path | CodeActionCommand | CodeActionCommand[], actionOrFormatSettingsOrUndefined?: CodeActionCommand | CodeActionCommand[] | FormatCodeSettings): Promise<ApplyCodeActionCommandResult | ApplyCodeActionCommandResult[]> {
2140            const action = typeof fileName === "string" ? actionOrFormatSettingsOrUndefined as CodeActionCommand | CodeActionCommand[] : fileName as CodeActionCommand[];
2141            return isArray(action) ? Promise.all(action.map(a => applySingleCodeActionCommand(a))) : applySingleCodeActionCommand(action);
2142        }
2143
2144        function applySingleCodeActionCommand(action: CodeActionCommand): Promise<ApplyCodeActionCommandResult> {
2145            const getPath = (path: string): Path => toPath(path, currentDirectory, getCanonicalFileName);
2146            Debug.assertEqual(action.type, "install package");
2147            return host.installPackage
2148                ? host.installPackage({ fileName: getPath(action.file), packageName: action.packageName })
2149                : Promise.reject("Host does not implement `installPackage`");
2150        }
2151
2152        function getDocCommentTemplateAtPosition(fileName: string, position: number, options?: DocCommentTemplateOptions): TextInsertion | undefined {
2153            return JsDoc.getDocCommentTemplateAtPosition(getNewLineOrDefaultFromHost(host), syntaxTreeCache.getCurrentSourceFile(fileName), position, options);
2154        }
2155
2156        function isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): boolean {
2157            // '<' is currently not supported, figuring out if we're in a Generic Type vs. a comparison is too
2158            // expensive to do during typing scenarios
2159            // i.e. whether we're dealing with:
2160            //      var x = new foo<| ( with class foo<T>{} )
2161            // or
2162            //      var y = 3 <|
2163            if (openingBrace === CharacterCodes.lessThan) {
2164                return false;
2165            }
2166
2167            const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
2168
2169            // Check if in a context where we don't want to perform any insertion
2170            if (isInString(sourceFile, position)) {
2171                return false;
2172            }
2173
2174            if (isInsideJsxElementOrAttribute(sourceFile, position)) {
2175                return openingBrace === CharacterCodes.openBrace;
2176            }
2177
2178            if (isInTemplateString(sourceFile, position)) {
2179                return false;
2180            }
2181
2182            switch (openingBrace) {
2183                case CharacterCodes.singleQuote:
2184                case CharacterCodes.doubleQuote:
2185                case CharacterCodes.backtick:
2186                    return !isInComment(sourceFile, position);
2187            }
2188
2189            return true;
2190        }
2191
2192        function getJsxClosingTagAtPosition(fileName: string, position: number): JsxClosingTagInfo | undefined {
2193            const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
2194            const token = findPrecedingToken(position, sourceFile);
2195            if (!token) return undefined;
2196            const element = token.kind === SyntaxKind.GreaterThanToken && isJsxOpeningElement(token.parent) ? token.parent.parent
2197                : isJsxText(token) && isJsxElement(token.parent) ? token.parent : undefined;
2198            if (element && isUnclosedTag(element)) {
2199                return { newText: `</${element.openingElement.tagName.getText(sourceFile)}>` };
2200            }
2201            const fragment = token.kind === SyntaxKind.GreaterThanToken && isJsxOpeningFragment(token.parent) ? token.parent.parent
2202                : isJsxText(token) && isJsxFragment(token.parent) ? token.parent : undefined;
2203            if (fragment && isUnclosedFragment(fragment)) {
2204                return { newText: "</>" };
2205            }
2206        }
2207
2208        function getLinesForRange(sourceFile: SourceFile, textRange: TextRange) {
2209            return {
2210                lineStarts: sourceFile.getLineStarts(),
2211                firstLine: sourceFile.getLineAndCharacterOfPosition(textRange.pos).line,
2212                lastLine: sourceFile.getLineAndCharacterOfPosition(textRange.end).line
2213            };
2214        }
2215
2216        function toggleLineComment(fileName: string, textRange: TextRange, insertComment?: boolean): TextChange[] {
2217            const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
2218            const textChanges: TextChange[] = [];
2219            const { lineStarts, firstLine, lastLine } = getLinesForRange(sourceFile, textRange);
2220
2221            let isCommenting = insertComment || false;
2222            let leftMostPosition = Number.MAX_VALUE;
2223            const lineTextStarts = new Map<string, number>();
2224            const firstNonWhitespaceCharacterRegex = new RegExp(/\S/);
2225            const isJsx = isInsideJsxElement(sourceFile, lineStarts[firstLine]);
2226            const openComment = isJsx ? "{/*" : "//";
2227
2228            // Check each line before any text changes.
2229            for (let i = firstLine; i <= lastLine; i++) {
2230                const lineText = sourceFile.text.substring(lineStarts[i], sourceFile.getLineEndOfPosition(lineStarts[i]));
2231
2232                // Find the start of text and the left-most character. No-op on empty lines.
2233                const regExec = firstNonWhitespaceCharacterRegex.exec(lineText);
2234                if (regExec) {
2235                    leftMostPosition = Math.min(leftMostPosition, regExec.index);
2236                    lineTextStarts.set(i.toString(), regExec.index);
2237
2238                    if (lineText.substr(regExec.index, openComment.length) !== openComment) {
2239                        isCommenting = insertComment === undefined || insertComment;
2240                    }
2241                }
2242            }
2243
2244            // Push all text changes.
2245            for (let i = firstLine; i <= lastLine; i++) {
2246                // If the range is multiline and ends on a beginning of a line, don't comment/uncomment.
2247                if (firstLine !== lastLine && lineStarts[i] === textRange.end) {
2248                    continue;
2249                }
2250
2251                const lineTextStart = lineTextStarts.get(i.toString());
2252
2253                // If the line is not an empty line; otherwise no-op.
2254                if (lineTextStart !== undefined) {
2255                    if (isJsx) {
2256                        textChanges.push.apply(textChanges, toggleMultilineComment(fileName, { pos: lineStarts[i] + leftMostPosition, end: sourceFile.getLineEndOfPosition(lineStarts[i]) }, isCommenting, isJsx));
2257                    }
2258                    else if (isCommenting) {
2259                        textChanges.push({
2260                            newText: openComment,
2261                            span: {
2262                                length: 0,
2263                                start: lineStarts[i] + leftMostPosition
2264                            }
2265                        });
2266                    }
2267                    else if (sourceFile.text.substr(lineStarts[i] + lineTextStart, openComment.length) === openComment) {
2268                        textChanges.push({
2269                            newText: "",
2270                            span: {
2271                                length: openComment.length,
2272                                start: lineStarts[i] + lineTextStart
2273                            }
2274                        });
2275                    }
2276                }
2277            }
2278
2279            return textChanges;
2280        }
2281
2282        function toggleMultilineComment(fileName: string, textRange: TextRange, insertComment?: boolean, isInsideJsx?: boolean): TextChange[] {
2283            const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
2284            const textChanges: TextChange[] = [];
2285            const { text } = sourceFile;
2286
2287            let hasComment = false;
2288            let isCommenting = insertComment || false;
2289            const positions = [] as number[] as SortedArray<number>;
2290
2291            let { pos } = textRange;
2292            const isJsx = isInsideJsx !== undefined ? isInsideJsx : isInsideJsxElement(sourceFile, pos);
2293
2294            const openMultiline = isJsx ? "{/*" : "/*";
2295            const closeMultiline = isJsx ? "*/}" : "*/";
2296            const openMultilineRegex = isJsx ? "\\{\\/\\*" : "\\/\\*";
2297            const closeMultilineRegex = isJsx ? "\\*\\/\\}" : "\\*\\/";
2298
2299            // Get all comment positions
2300            while (pos <= textRange.end) {
2301                // Start of comment is considered inside comment.
2302                const offset = text.substr(pos, openMultiline.length) === openMultiline ? openMultiline.length : 0;
2303                const commentRange = isInComment(sourceFile, pos + offset);
2304
2305                // If position is in a comment add it to the positions array.
2306                if (commentRange) {
2307                    // Comment range doesn't include the brace character. Increase it to include them.
2308                    if (isJsx) {
2309                        commentRange.pos--;
2310                        commentRange.end++;
2311                    }
2312
2313                    positions.push(commentRange.pos);
2314                    if (commentRange.kind === SyntaxKind.MultiLineCommentTrivia) {
2315                        positions.push(commentRange.end);
2316                    }
2317
2318                    hasComment = true;
2319                    pos = commentRange.end + 1;
2320                }
2321                else { // If it's not in a comment range, then we need to comment the uncommented portions.
2322                    const newPos = text.substring(pos, textRange.end).search(`(${openMultilineRegex})|(${closeMultilineRegex})`);
2323
2324                    isCommenting = insertComment !== undefined
2325                        ? insertComment
2326                        : isCommenting || !isTextWhiteSpaceLike(text, pos, newPos === -1 ? textRange.end : pos + newPos); // If isCommenting is already true we don't need to check whitespace again.
2327                    pos = newPos === -1 ? textRange.end + 1 : pos + newPos + closeMultiline.length;
2328                }
2329            }
2330
2331            // If it didn't found a comment and isCommenting is false means is only empty space.
2332            // We want to insert comment in this scenario.
2333            if (isCommenting || !hasComment) {
2334                if (isInComment(sourceFile, textRange.pos)?.kind !== SyntaxKind.SingleLineCommentTrivia) {
2335                    insertSorted(positions, textRange.pos, compareValues);
2336                }
2337                insertSorted(positions, textRange.end, compareValues);
2338
2339                // Insert open comment if the first position is not a comment already.
2340                const firstPos = positions[0];
2341                if (text.substr(firstPos, openMultiline.length) !== openMultiline) {
2342                    textChanges.push({
2343                        newText: openMultiline,
2344                        span: {
2345                            length: 0,
2346                            start: firstPos
2347                        }
2348                    });
2349                }
2350
2351                // Insert open and close comment to all positions between first and last. Exclusive.
2352                for (let i = 1; i < positions.length - 1; i++) {
2353                    if (text.substr(positions[i] - closeMultiline.length, closeMultiline.length) !== closeMultiline) {
2354                        textChanges.push({
2355                            newText: closeMultiline,
2356                            span: {
2357                                length: 0,
2358                                start: positions[i]
2359                            }
2360                        });
2361                    }
2362
2363                    if (text.substr(positions[i], openMultiline.length) !== openMultiline) {
2364                        textChanges.push({
2365                            newText: openMultiline,
2366                            span: {
2367                                length: 0,
2368                                start: positions[i]
2369                            }
2370                        });
2371                    }
2372                }
2373
2374                // Insert open comment if the last position is not a comment already.
2375                if (textChanges.length % 2 !== 0) {
2376                    textChanges.push({
2377                        newText: closeMultiline,
2378                        span: {
2379                            length: 0,
2380                            start: positions[positions.length - 1]
2381                        }
2382                    });
2383                }
2384            }
2385            else {
2386                // If is not commenting then remove all comments found.
2387                for (const pos of positions) {
2388                    const from = pos - closeMultiline.length > 0 ? pos - closeMultiline.length : 0;
2389                    const offset = text.substr(from, closeMultiline.length) === closeMultiline ? closeMultiline.length : 0;
2390                    textChanges.push({
2391                        newText: "",
2392                        span: {
2393                            length: openMultiline.length,
2394                            start: pos - offset
2395                        }
2396                    });
2397                }
2398            }
2399
2400            return textChanges;
2401        }
2402
2403        function commentSelection(fileName: string, textRange: TextRange): TextChange[] {
2404            const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
2405            const { firstLine, lastLine } = getLinesForRange(sourceFile, textRange);
2406
2407            // If there is a selection that is on the same line, add multiline.
2408            return firstLine === lastLine && textRange.pos !== textRange.end
2409                ? toggleMultilineComment(fileName, textRange, /*insertComment*/ true)
2410                : toggleLineComment(fileName, textRange, /*insertComment*/ true);
2411        }
2412
2413        function uncommentSelection(fileName: string, textRange: TextRange): TextChange[] {
2414            const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
2415            const textChanges: TextChange[] = [];
2416            const { pos } = textRange;
2417            let { end } = textRange;
2418
2419            // If cursor is not a selection we need to increase the end position
2420            // to include the start of the comment.
2421            if (pos === end) {
2422                end += isInsideJsxElement(sourceFile, pos) ? 2 : 1;
2423            }
2424
2425            for (let i = pos; i <= end; i++) {
2426                const commentRange = isInComment(sourceFile, i);
2427                if (commentRange) {
2428                    switch (commentRange.kind) {
2429                        case SyntaxKind.SingleLineCommentTrivia:
2430                            textChanges.push.apply(textChanges, toggleLineComment(fileName, { end: commentRange.end, pos: commentRange.pos + 1 }, /*insertComment*/ false));
2431                            break;
2432                        case SyntaxKind.MultiLineCommentTrivia:
2433                            textChanges.push.apply(textChanges, toggleMultilineComment(fileName, { end: commentRange.end, pos: commentRange.pos + 1 }, /*insertComment*/ false));
2434                    }
2435
2436                    i = commentRange.end + 1;
2437                }
2438            }
2439
2440            return textChanges;
2441        }
2442
2443        function isUnclosedTag({ openingElement, closingElement, parent }: JsxElement): boolean {
2444            return !tagNamesAreEquivalent(openingElement.tagName, closingElement.tagName) ||
2445                isJsxElement(parent) && tagNamesAreEquivalent(openingElement.tagName, parent.openingElement.tagName) && isUnclosedTag(parent);
2446        }
2447
2448        function isUnclosedFragment({ closingFragment, parent }: JsxFragment): boolean {
2449            return !!(closingFragment.flags & NodeFlags.ThisNodeHasError) || (isJsxFragment(parent) && isUnclosedFragment(parent));
2450        }
2451
2452        function getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan | undefined {
2453            const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
2454            const range = formatting.getRangeOfEnclosingComment(sourceFile, position);
2455            return range && (!onlyMultiLine || range.kind === SyntaxKind.MultiLineCommentTrivia) ? createTextSpanFromRange(range) : undefined;
2456        }
2457
2458        function getTodoComments(fileName: string, descriptors: TodoCommentDescriptor[]): TodoComment[] {
2459            // Note: while getting todo comments seems like a syntactic operation, we actually
2460            // treat it as a semantic operation here.  This is because we expect our host to call
2461            // this on every single file.  If we treat this syntactically, then that will cause
2462            // us to populate and throw away the tree in our syntax tree cache for each file.  By
2463            // treating this as a semantic operation, we can access any tree without throwing
2464            // anything away.
2465            synchronizeHostData();
2466
2467            const sourceFile = getValidSourceFile(fileName);
2468
2469            cancellationToken.throwIfCancellationRequested();
2470
2471            const fileContents = sourceFile.text;
2472            const result: TodoComment[] = [];
2473
2474            // Exclude node_modules or oh_modules files as we don't want to show the todos of external libraries.
2475            if (descriptors.length > 0 && !isNodeModulesFile(sourceFile.fileName) && !isOHModulesFile(sourceFile.fileName)) {
2476                const regExp = getTodoCommentsRegExp();
2477
2478                let matchArray: RegExpExecArray | null;
2479                while (matchArray = regExp.exec(fileContents)) {
2480                    cancellationToken.throwIfCancellationRequested();
2481
2482                    // If we got a match, here is what the match array will look like.  Say the source text is:
2483                    //
2484                    //      "    // hack   1"
2485                    //
2486                    // The result array with the regexp:    will be:
2487                    //
2488                    //      ["// hack   1", "// ", "hack   1", undefined, "hack"]
2489                    //
2490                    // Here are the relevant capture groups:
2491                    //  0) The full match for the entire regexp.
2492                    //  1) The preamble to the message portion.
2493                    //  2) The message portion.
2494                    //  3...N) The descriptor that was matched - by index.  'undefined' for each
2495                    //         descriptor that didn't match.  an actual value if it did match.
2496                    //
2497                    //  i.e. 'undefined' in position 3 above means TODO(jason) didn't match.
2498                    //       "hack"      in position 4 means HACK did match.
2499                    const firstDescriptorCaptureIndex = 3;
2500                    Debug.assert(matchArray.length === descriptors.length + firstDescriptorCaptureIndex);
2501
2502                    const preamble = matchArray[1];
2503                    const matchPosition = matchArray.index + preamble.length;
2504
2505                    // OK, we have found a match in the file.  This is only an acceptable match if
2506                    // it is contained within a comment.
2507                    if (!isInComment(sourceFile, matchPosition)) {
2508                        continue;
2509                    }
2510
2511                    let descriptor: TodoCommentDescriptor | undefined;
2512                    for (let i = 0; i < descriptors.length; i++) {
2513                        if (matchArray[i + firstDescriptorCaptureIndex]) {
2514                            descriptor = descriptors[i];
2515                        }
2516                    }
2517                    if (descriptor === undefined) return Debug.fail();
2518
2519                    // We don't want to match something like 'TODOBY', so we make sure a non
2520                    // letter/digit follows the match.
2521                    if (isLetterOrDigit(fileContents.charCodeAt(matchPosition + descriptor.text.length))) {
2522                        continue;
2523                    }
2524
2525                    const message = matchArray[2];
2526                    result.push({ descriptor, message, position: matchPosition });
2527                }
2528            }
2529
2530            return result;
2531
2532            function escapeRegExp(str: string): string {
2533                return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
2534            }
2535
2536            function getTodoCommentsRegExp(): RegExp {
2537                // NOTE: `?:` means 'non-capture group'.  It allows us to have groups without having to
2538                // filter them out later in the final result array.
2539
2540                // TODO comments can appear in one of the following forms:
2541                //
2542                //  1)      // TODO     or  /////////// TODO
2543                //
2544                //  2)      /* TODO     or  /********** TODO
2545                //
2546                //  3)      /*
2547                //           *   TODO
2548                //           */
2549                //
2550                // The following three regexps are used to match the start of the text up to the TODO
2551                // comment portion.
2552                const singleLineCommentStart = /(?:\/\/+\s*)/.source;
2553                const multiLineCommentStart = /(?:\/\*+\s*)/.source;
2554                const anyNumberOfSpacesAndAsterisksAtStartOfLine = /(?:^(?:\s|\*)*)/.source;
2555
2556                // Match any of the above three TODO comment start regexps.
2557                // Note that the outermost group *is* a capture group.  We want to capture the preamble
2558                // so that we can determine the starting position of the TODO comment match.
2559                const preamble = "(" + anyNumberOfSpacesAndAsterisksAtStartOfLine + "|" + singleLineCommentStart + "|" + multiLineCommentStart + ")";
2560
2561                // Takes the descriptors and forms a regexp that matches them as if they were literals.
2562                // For example, if the descriptors are "TODO(jason)" and "HACK", then this will be:
2563                //
2564                //      (?:(TODO\(jason\))|(HACK))
2565                //
2566                // Note that the outermost group is *not* a capture group, but the innermost groups
2567                // *are* capture groups.  By capturing the inner literals we can determine after
2568                // matching which descriptor we are dealing with.
2569                const literals = "(?:" + map(descriptors, d => "(" + escapeRegExp(d.text) + ")").join("|") + ")";
2570
2571                // After matching a descriptor literal, the following regexp matches the rest of the
2572                // text up to the end of the line (or */).
2573                const endOfLineOrEndOfComment = /(?:$|\*\/)/.source;
2574                const messageRemainder = /(?:.*?)/.source;
2575
2576                // This is the portion of the match we'll return as part of the TODO comment result. We
2577                // match the literal portion up to the end of the line or end of comment.
2578                const messagePortion = "(" + literals + messageRemainder + ")";
2579                const regExpString = preamble + messagePortion + endOfLineOrEndOfComment;
2580
2581                // The final regexp will look like this:
2582                // /((?:\/\/+\s*)|(?:\/\*+\s*)|(?:^(?:\s|\*)*))((?:(TODO\(jason\))|(HACK))(?:.*?))(?:$|\*\/)/gim
2583
2584                // The flags of the regexp are important here.
2585                //  'g' is so that we are doing a global search and can find matches several times
2586                //  in the input.
2587                //
2588                //  'i' is for case insensitivity (We do this to match C# TODO comment code).
2589                //
2590                //  'm' is so we can find matches in a multi-line input.
2591                return new RegExp(regExpString, "gim");
2592            }
2593
2594            function isLetterOrDigit(char: number): boolean {
2595                return (char >= CharacterCodes.a && char <= CharacterCodes.z) ||
2596                    (char >= CharacterCodes.A && char <= CharacterCodes.Z) ||
2597                    (char >= CharacterCodes._0 && char <= CharacterCodes._9);
2598            }
2599
2600            function isNodeModulesFile(path: string): boolean {
2601                return stringContains(path, "/node_modules/");
2602            }
2603
2604            function isOHModulesFile(path: string): boolean {
2605                return stringContains(path, "/oh_modules/");
2606            }
2607        }
2608
2609        function getRenameInfo(fileName: string, position: number, preferences: UserPreferences | RenameInfoOptions | undefined): RenameInfo {
2610            synchronizeHostData();
2611            return Rename.getRenameInfo(program, getValidSourceFile(fileName), position, preferences || {});
2612        }
2613
2614        function getRefactorContext(file: SourceFile, positionOrRange: number | TextRange, preferences: UserPreferences, formatOptions?: FormatCodeSettings, triggerReason?: RefactorTriggerReason, kind?: string): RefactorContext {
2615            const [startPosition, endPosition] = typeof positionOrRange === "number" ? [positionOrRange, undefined] : [positionOrRange.pos, positionOrRange.end];
2616            return {
2617                file,
2618                startPosition,
2619                endPosition,
2620                program: getProgram()!,
2621                host,
2622                formatContext: formatting.getFormatContext(formatOptions!, host), // TODO: GH#18217
2623                cancellationToken,
2624                preferences,
2625                triggerReason,
2626                kind
2627            };
2628        }
2629
2630        function getInlayHintsContext(file: SourceFile, span: TextSpan, preferences: UserPreferences): InlayHintsContext {
2631            return {
2632                file,
2633                program: getProgram()!,
2634                host,
2635                span,
2636                preferences,
2637                cancellationToken,
2638            };
2639        }
2640
2641        function getSmartSelectionRange(fileName: string, position: number): SelectionRange {
2642            return SmartSelectionRange.getSmartSelectionRange(position, syntaxTreeCache.getCurrentSourceFile(fileName));
2643        }
2644
2645        function getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences = emptyOptions, triggerReason: RefactorTriggerReason, kind: string): ApplicableRefactorInfo[] {
2646            synchronizeHostData();
2647            const file = getValidSourceFile(fileName);
2648            return refactor.getApplicableRefactors(getRefactorContext(file, positionOrRange, preferences, emptyOptions, triggerReason, kind));
2649        }
2650
2651        function getEditsForRefactor(
2652            fileName: string,
2653            formatOptions: FormatCodeSettings,
2654            positionOrRange: number | TextRange,
2655            refactorName: string,
2656            actionName: string,
2657            preferences: UserPreferences = emptyOptions,
2658        ): RefactorEditInfo | undefined {
2659            synchronizeHostData();
2660            const file = getValidSourceFile(fileName);
2661            return refactor.getEditsForRefactor(getRefactorContext(file, positionOrRange, preferences, formatOptions), refactorName, actionName);
2662        }
2663
2664        function toLineColumnOffset(fileName: string, position: number): LineAndCharacter {
2665            // Go to Definition supports returning a zero-length span at position 0 for
2666            // non-existent files. We need to special-case the conversion of position 0
2667            // to avoid a crash trying to get the text for that file, since this function
2668            // otherwise assumes that 'fileName' is the name of a file that exists.
2669            if (position === 0) {
2670                return { line: 0, character: 0 };
2671            }
2672            return sourceMapper.toLineColumnOffset(fileName, position);
2673        }
2674
2675        function prepareCallHierarchy(fileName: string, position: number): CallHierarchyItem | CallHierarchyItem[] | undefined {
2676            synchronizeHostData();
2677            const declarations = CallHierarchy.resolveCallHierarchyDeclaration(program, getTouchingPropertyName(getValidSourceFile(fileName), position));
2678            return declarations && mapOneOrMany(declarations, declaration => CallHierarchy.createCallHierarchyItem(program, declaration));
2679        }
2680
2681        function provideCallHierarchyIncomingCalls(fileName: string, position: number): CallHierarchyIncomingCall[] {
2682            synchronizeHostData();
2683            const sourceFile = getValidSourceFile(fileName);
2684            const declaration = firstOrOnly(CallHierarchy.resolveCallHierarchyDeclaration(program, position === 0 ? sourceFile : getTouchingPropertyName(sourceFile, position)));
2685            return declaration ? CallHierarchy.getIncomingCalls(program, declaration, cancellationToken) : [];
2686        }
2687
2688        function provideCallHierarchyOutgoingCalls(fileName: string, position: number): CallHierarchyOutgoingCall[] {
2689            synchronizeHostData();
2690            const sourceFile = getValidSourceFile(fileName);
2691            const declaration = firstOrOnly(CallHierarchy.resolveCallHierarchyDeclaration(program, position === 0 ? sourceFile : getTouchingPropertyName(sourceFile, position)));
2692            return declaration ? CallHierarchy.getOutgoingCalls(program, declaration) : [];
2693        }
2694
2695        function provideInlayHints(fileName: string, span: TextSpan, preferences: UserPreferences = emptyOptions): InlayHint[] {
2696            synchronizeHostData();
2697            const sourceFile = getValidSourceFile(fileName);
2698            return InlayHints.provideInlayHints(getInlayHintsContext(sourceFile, span, preferences));
2699        }
2700
2701        function updateRootFiles(rootFiles: string[]) {
2702            host.getScriptFileNames = () => rootFiles
2703        }
2704
2705        function getProps(): string[] {
2706            return host.uiProps ? host.uiProps : [];
2707        }
2708
2709        const ls: LanguageService = {
2710            dispose,
2711            cleanupSemanticCache,
2712            getSyntacticDiagnostics,
2713            getSemanticDiagnostics,
2714            getSuggestionDiagnostics,
2715            getCompilerOptionsDiagnostics,
2716            getSyntacticClassifications,
2717            getSemanticClassifications,
2718            getEncodedSyntacticClassifications,
2719            getEncodedSemanticClassifications,
2720            getCompletionsAtPosition,
2721            getCompletionEntryDetails,
2722            getCompletionEntrySymbol,
2723            getSignatureHelpItems,
2724            getQuickInfoAtPosition,
2725            getDefinitionAtPosition,
2726            getDefinitionAndBoundSpan,
2727            getImplementationAtPosition,
2728            getTypeDefinitionAtPosition,
2729            getReferencesAtPosition,
2730            findReferences,
2731            getFileReferences,
2732            getOccurrencesAtPosition,
2733            getDocumentHighlights,
2734            getNameOrDottedNameSpan,
2735            getBreakpointStatementAtPosition,
2736            getNavigateToItems,
2737            getRenameInfo,
2738            getSmartSelectionRange,
2739            findRenameLocations,
2740            getNavigationBarItems,
2741            getNavigationTree,
2742            getOutliningSpans,
2743            getTodoComments,
2744            getBraceMatchingAtPosition,
2745            getIndentationAtPosition,
2746            getFormattingEditsForRange,
2747            getFormattingEditsForDocument,
2748            getFormattingEditsAfterKeystroke,
2749            getDocCommentTemplateAtPosition,
2750            isValidBraceCompletionAtPosition,
2751            getJsxClosingTagAtPosition,
2752            getSpanOfEnclosingComment,
2753            getCodeFixesAtPosition,
2754            getCombinedCodeFix,
2755            applyCodeActionCommand,
2756            organizeImports,
2757            getEditsForFileRename,
2758            getEmitOutput,
2759            getNonBoundSourceFile,
2760            getProgram,
2761            getBuilderProgram,
2762            getCurrentProgram: () => program,
2763            getAutoImportProvider,
2764            updateIsDefinitionOfReferencedSymbols,
2765            getApplicableRefactors,
2766            getEditsForRefactor,
2767            toLineColumnOffset,
2768            getSourceMapper: () => sourceMapper,
2769            clearSourceMapperCache: () => sourceMapper.clearCache(),
2770            prepareCallHierarchy,
2771            provideCallHierarchyIncomingCalls,
2772            provideCallHierarchyOutgoingCalls,
2773            toggleLineComment,
2774            toggleMultilineComment,
2775            commentSelection,
2776            uncommentSelection,
2777            provideInlayHints,
2778            updateRootFiles,
2779            getProps
2780        };
2781
2782        switch (languageServiceMode) {
2783            case LanguageServiceMode.Semantic:
2784                break;
2785            case LanguageServiceMode.PartialSemantic:
2786                invalidOperationsInPartialSemanticMode.forEach(key =>
2787                    ls[key] = () => {
2788                        throw new Error(`LanguageService Operation: ${key} not allowed in LanguageServiceMode.PartialSemantic`);
2789                    }
2790                );
2791                break;
2792            case LanguageServiceMode.Syntactic:
2793                invalidOperationsInSyntacticMode.forEach(key =>
2794                    ls[key] = () => {
2795                        throw new Error(`LanguageService Operation: ${key} not allowed in LanguageServiceMode.Syntactic`);
2796                    }
2797                );
2798                break;
2799            default:
2800                Debug.assertNever(languageServiceMode);
2801        }
2802        return ls;
2803    }
2804
2805    /* @internal */
2806    /** Names in the name table are escaped, so an identifier `__foo` will have a name table entry `___foo`. */
2807    export function getNameTable(sourceFile: SourceFile): UnderscoreEscapedMap<number> {
2808        if (!sourceFile.nameTable) {
2809            initializeNameTable(sourceFile);
2810        }
2811
2812        return sourceFile.nameTable!; // TODO: GH#18217
2813    }
2814
2815    function initializeNameTable(sourceFile: SourceFile): void {
2816        const nameTable = sourceFile.nameTable = new Map();
2817        sourceFile.forEachChild(function walk(node) {
2818            if (isIdentifier(node) && !isTagName(node) && node.escapedText || isStringOrNumericLiteralLike(node) && literalIsName(node)) {
2819                const text = getEscapedTextOfIdentifierOrLiteral(node);
2820                nameTable.set(text, nameTable.get(text) === undefined ? node.pos : -1);
2821            }
2822            else if (isPrivateIdentifier(node)) {
2823                const text = node.escapedText;
2824                nameTable.set(text, nameTable.get(text) === undefined ? node.pos : -1);
2825            }
2826
2827            forEachChild(node, walk);
2828            if (hasJSDocNodes(node)) {
2829                for (const jsDoc of node.jsDoc!) {
2830                    forEachChild(jsDoc, walk);
2831                }
2832            }
2833        });
2834    }
2835
2836    /**
2837     * We want to store any numbers/strings if they were a name that could be
2838     * related to a declaration.  So, if we have 'import x = require("something")'
2839     * then we want 'something' to be in the name table.  Similarly, if we have
2840     * "a['propname']" then we want to store "propname" in the name table.
2841     */
2842    function literalIsName(node: StringLiteralLike | NumericLiteral): boolean {
2843        return isDeclarationName(node) ||
2844            node.parent.kind === SyntaxKind.ExternalModuleReference ||
2845            isArgumentOfElementAccessExpression(node) ||
2846            isLiteralComputedPropertyDeclarationName(node);
2847    }
2848
2849    /**
2850     * Returns the containing object literal property declaration given a possible name node, e.g. "a" in x = { "a": 1 }
2851     */
2852    /* @internal */
2853    export function getContainingObjectLiteralElement(node: Node): ObjectLiteralElementWithName | undefined {
2854        const element = getContainingObjectLiteralElementWorker(node);
2855        return element && (isObjectLiteralExpression(element.parent) || isJsxAttributes(element.parent)) ? element as ObjectLiteralElementWithName : undefined;
2856    }
2857    function getContainingObjectLiteralElementWorker(node: Node): ObjectLiteralElement | undefined {
2858        switch (node.kind) {
2859            case SyntaxKind.StringLiteral:
2860            case SyntaxKind.NoSubstitutionTemplateLiteral:
2861            case SyntaxKind.NumericLiteral:
2862                if (node.parent.kind === SyntaxKind.ComputedPropertyName) {
2863                    return isObjectLiteralElement(node.parent.parent) ? node.parent.parent : undefined;
2864                }
2865            // falls through
2866
2867            case SyntaxKind.Identifier:
2868                return isObjectLiteralElement(node.parent) &&
2869                    (node.parent.parent.kind === SyntaxKind.ObjectLiteralExpression || node.parent.parent.kind === SyntaxKind.JsxAttributes) &&
2870                    node.parent.name === node ? node.parent : undefined;
2871        }
2872        return undefined;
2873    }
2874
2875    /* @internal */
2876    export type ObjectLiteralElementWithName = ObjectLiteralElement & { name: PropertyName; parent: ObjectLiteralExpression | JsxAttributes };
2877
2878    function getSymbolAtLocationForQuickInfo(node: Node, checker: TypeChecker): Symbol | undefined {
2879        const object = getContainingObjectLiteralElement(node);
2880        if (object) {
2881            const contextualType = checker.getContextualType(object.parent);
2882            const properties = contextualType && getPropertySymbolsFromContextualType(object, checker, contextualType, /*unionSymbolOk*/ false);
2883            if (properties && properties.length === 1) {
2884                return first(properties);
2885            }
2886        }
2887        return checker.getSymbolAtLocation(node);
2888    }
2889
2890    /** Gets all symbols for one property. Does not get symbols for every property. */
2891    /* @internal */
2892    export function getPropertySymbolsFromContextualType(node: ObjectLiteralElementWithName, checker: TypeChecker, contextualType: Type, unionSymbolOk: boolean): readonly Symbol[] {
2893        const name = getNameFromPropertyName(node.name);
2894        if (!name) return emptyArray;
2895        if (!contextualType.isUnion()) {
2896            const symbol = contextualType.getProperty(name);
2897            return symbol ? [symbol] : emptyArray;
2898        }
2899
2900        const discriminatedPropertySymbols = mapDefined(contextualType.types, t => (isObjectLiteralExpression(node.parent)|| isJsxAttributes(node.parent)) && checker.isTypeInvalidDueToUnionDiscriminant(t, node.parent) ? undefined : t.getProperty(name));
2901        if (unionSymbolOk && (discriminatedPropertySymbols.length === 0 || discriminatedPropertySymbols.length === contextualType.types.length)) {
2902            const symbol = contextualType.getProperty(name);
2903            if (symbol) return [symbol];
2904        }
2905        if (discriminatedPropertySymbols.length === 0) {
2906            // Bad discriminant -- do again without discriminating
2907            return mapDefined(contextualType.types, t => t.getProperty(name));
2908        }
2909        return discriminatedPropertySymbols;
2910    }
2911
2912    function isArgumentOfElementAccessExpression(node: Node) {
2913        return node &&
2914            node.parent &&
2915            node.parent.kind === SyntaxKind.ElementAccessExpression &&
2916            (node.parent as ElementAccessExpression).argumentExpression === node;
2917    }
2918
2919    /// getDefaultLibraryFilePath
2920    declare const __dirname: string;
2921
2922    /**
2923     * Get the path of the default library files (lib.d.ts) as distributed with the typescript
2924     * node package.
2925     * The functionality is not supported if the ts module is consumed outside of a node module.
2926     */
2927    export function getDefaultLibFilePath(options: CompilerOptions): string {
2928        // Check __dirname is defined and that we are on a node.js system.
2929        if (typeof __dirname !== "undefined") {
2930            return combinePaths(__dirname, getDefaultLibFileName(options));
2931        }
2932
2933        throw new Error("getDefaultLibFilePath is only supported when consumed as a node module. ");
2934    }
2935
2936    setObjectAllocator(getServicesObjectAllocator());
2937}
2938