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