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