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