• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1//
2// Copyright (c) Microsoft Corporation.  All rights reserved.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//   http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15
16/* @internal */
17let debugObjectHost: { CollectGarbage(): void } = (function (this: any) { // eslint-disable-line prefer-const
18    return this;
19})();
20
21// We need to use 'null' to interface with the managed side.
22/* eslint-disable local/no-in-operator */
23
24/* @internal */
25namespace ts {
26    interface DiscoverTypingsInfo {
27        fileNames: string[];                            // The file names that belong to the same project.
28        projectRootPath: string;                        // The path to the project root directory
29        safeListPath: string;                           // The path used to retrieve the safe list
30        packageNameToTypingLocation: ESMap<string, JsTyping.CachedTyping>;       // The map of package names to their cached typing locations and installed versions
31        typeAcquisition: TypeAcquisition;               // Used to customize the type acquisition process
32        compilerOptions: CompilerOptions;               // Used as a source for typing inference
33        unresolvedImports: readonly string[];       // List of unresolved module ids from imports
34        typesRegistry: ReadonlyESMap<string, MapLike<string>>;    // The map of available typings in npm to maps of TS versions to their latest supported versions
35    }
36
37    export interface ScriptSnapshotShim {
38        /** Gets a portion of the script snapshot specified by [start, end). */
39        getText(start: number, end: number): string;
40
41        /** Gets the length of this script snapshot. */
42        getLength(): number;
43
44        /**
45         * Returns a JSON-encoded value of the type:
46         *   { span: { start: number; length: number }; newLength: number }
47         *
48         * Or undefined value if there was no change.
49         */
50        getChangeRange(oldSnapshot: ScriptSnapshotShim): string | undefined;
51
52        /** Releases all resources held by this script snapshot */
53        dispose?(): void;
54    }
55
56    export interface Logger {
57        log(s: string): void;
58        trace(s: string): void;
59        error(s: string): void;
60    }
61
62    /** Public interface of the host of a language service shim instance. */
63    export interface LanguageServiceShimHost extends Logger {
64        getCompilationSettings(): string;
65
66        /** Returns a JSON-encoded value of the type: string[] */
67        getScriptFileNames(): string;
68        getScriptKind?(fileName: string): ScriptKind;
69        getScriptVersion(fileName: string): string;
70        getScriptSnapshot(fileName: string): ScriptSnapshotShim;
71        getLocalizedDiagnosticMessages(): string;
72        getCancellationToken(): HostCancellationToken;
73        getCurrentDirectory(): string;
74        getDirectories(path: string): string;
75        getDefaultLibFileName(options: string): string;
76        getNewLine?(): string;
77        getProjectVersion?(): string;
78        useCaseSensitiveFileNames?(): boolean;
79
80        getTypeRootsVersion?(): number;
81        readDirectory(rootDir: string, extension: string, basePaths?: string, excludeEx?: string, includeFileEx?: string, includeDirEx?: string, depth?: number): string;
82        readFile(path: string, encoding?: string): string | undefined;
83        fileExists(path: string): boolean;
84
85        getModuleResolutionsForFile?(fileName: string): string;
86        getTypeReferenceDirectiveResolutionsForFile?(fileName: string): string;
87        directoryExists(directoryName: string): boolean;
88    }
89
90    /** Public interface of the core-services host instance used in managed side */
91    export interface CoreServicesShimHost extends Logger {
92        directoryExists(directoryName: string): boolean;
93        fileExists(fileName: string): boolean;
94        getCurrentDirectory(): string;
95        getDirectories(path: string): string;
96
97        /**
98         * Returns a JSON-encoded value of the type: string[]
99         *
100         * @param exclude A JSON encoded string[] containing the paths to exclude
101         *  when enumerating the directory.
102         */
103        readDirectory(rootDir: string, extension: string, basePaths?: string, excludeEx?: string, includeFileEx?: string, includeDirEx?: string, depth?: number): string;
104
105        /**
106         * Read arbitrary text files on disk, i.e. when resolution procedure needs the content of 'package.json' to determine location of bundled typings for node modules
107         */
108        readFile(fileName: string): string | undefined;
109        realpath?(path: string): string;
110        trace(s: string): void;
111        useCaseSensitiveFileNames?(): boolean;
112    }
113
114    ///
115    /// Pre-processing
116    ///
117    // Note: This is being using by the host (VS) and is marshaled back and forth.
118    // When changing this make sure the changes are reflected in the managed side as well
119    export interface ShimsFileReference {
120        path: string;
121        position: number;
122        length: number;
123    }
124
125    /** Public interface of a language service instance shim. */
126    export interface ShimFactory {
127        registerShim(shim: Shim): void;
128        unregisterShim(shim: Shim): void;
129    }
130
131    export interface Shim {
132        dispose(_dummy: {}): void;
133    }
134
135    export interface LanguageServiceShim extends Shim {
136        languageService: LanguageService;
137
138        dispose(_dummy: {}): void;
139
140        refresh(throwOnError: boolean): void;
141
142        cleanupSemanticCache(): void;
143
144        getSyntacticDiagnostics(fileName: string): string;
145        getSemanticDiagnostics(fileName: string): string;
146        getSuggestionDiagnostics(fileName: string): string;
147        getCompilerOptionsDiagnostics(): string;
148
149        getSyntacticClassifications(fileName: string, start: number, length: number): string;
150        getSemanticClassifications(fileName: string, start: number, length: number, format?: SemanticClassificationFormat): string;
151        getEncodedSyntacticClassifications(fileName: string, start: number, length: number): string;
152        getEncodedSemanticClassifications(fileName: string, start: number, length: number, format?: SemanticClassificationFormat): string;
153
154        getCompletionsAtPosition(fileName: string, position: number, preferences: UserPreferences | undefined, formattingSettings: FormatCodeSettings | undefined): string;
155        getCompletionEntryDetails(fileName: string, position: number, entryName: string, formatOptions: string/*Services.FormatCodeOptions*/ | undefined, source: string | undefined, preferences: UserPreferences | undefined, data: CompletionEntryData | undefined): string;
156
157        getQuickInfoAtPosition(fileName: string, position: number): string;
158
159        getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): string;
160        getBreakpointStatementAtPosition(fileName: string, position: number): string;
161
162        getSignatureHelpItems(fileName: string, position: number, options: SignatureHelpItemsOptions | undefined): string;
163
164        /**
165         * Returns a JSON-encoded value of the type:
166         * { canRename: boolean, localizedErrorMessage: string, displayName: string, fullDisplayName: string, kind: string, kindModifiers: string, triggerSpan: { start; length } }
167         */
168        getRenameInfo(fileName: string, position: number, preferences: UserPreferences): string;
169        getSmartSelectionRange(fileName: string, position: number): string;
170
171        /**
172         * Returns a JSON-encoded value of the type:
173         * { fileName: string, textSpan: { start: number, length: number } }[]
174         */
175        findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): string;
176
177        /**
178         * Returns a JSON-encoded value of the type:
179         * { fileName: string; textSpan: { start: number; length: number}; kind: string; name: string; containerKind: string; containerName: string }
180         *
181         * Or undefined value if no definition can be found.
182         */
183        getDefinitionAtPosition(fileName: string, position: number): string;
184
185        getDefinitionAndBoundSpan(fileName: string, position: number): string;
186
187        /**
188         * Returns a JSON-encoded value of the type:
189         * { fileName: string; textSpan: { start: number; length: number}; kind: string; name: string; containerKind: string; containerName: string }
190         *
191         * Or undefined value if no definition can be found.
192         */
193        getTypeDefinitionAtPosition(fileName: string, position: number): string;
194
195        /**
196         * Returns a JSON-encoded value of the type:
197         * { fileName: string; textSpan: { start: number; length: number}; }[]
198         */
199        getImplementationAtPosition(fileName: string, position: number): string;
200
201        /**
202         * Returns a JSON-encoded value of the type:
203         * { fileName: string; textSpan: { start: number; length: number}; isWriteAccess: boolean, isDefinition?: boolean }[]
204         */
205        getReferencesAtPosition(fileName: string, position: number): string;
206
207        /**
208         * Returns a JSON-encoded value of the type:
209         * { definition: <encoded>; references: <encoded>[] }[]
210         */
211        findReferences(fileName: string, position: number): string;
212
213        /**
214         * Returns a JSON-encoded value of the type:
215         * { fileName: string; textSpan: { start: number; length: number}; isWriteAccess: boolean, isDefinition?: boolean }[]
216         */
217        getFileReferences(fileName: string): string;
218
219        /**
220         * @deprecated
221         * Returns a JSON-encoded value of the type:
222         * { fileName: string; textSpan: { start: number; length: number}; isWriteAccess: boolean }[]
223         */
224        getOccurrencesAtPosition(fileName: string, position: number): string;
225
226        /**
227         * Returns a JSON-encoded value of the type:
228         * { fileName: string; highlights: { start: number; length: number }[] }[]
229         *
230         * @param fileToSearch A JSON encoded string[] containing the file names that should be
231         *  considered when searching.
232         */
233        getDocumentHighlights(fileName: string, position: number, filesToSearch: string): string;
234
235        /**
236         * Returns a JSON-encoded value of the type:
237         * { name: string; kind: string; kindModifiers: string; containerName: string; containerKind: string; matchKind: string; fileName: string; textSpan: { start: number; length: number}; } [] = [];
238         */
239        getNavigateToItems(searchValue: string, maxResultCount?: number, fileName?: string): string;
240
241        /**
242         * Returns a JSON-encoded value of the type:
243         * { text: string; kind: string; kindModifiers: string; bolded: boolean; grayed: boolean; indent: number; spans: { start: number; length: number; }[]; childItems: <recursive use of this type>[] } [] = [];
244         */
245        getNavigationBarItems(fileName: string): string;
246
247        /** Returns a JSON-encoded value of the type ts.NavigationTree. */
248        getNavigationTree(fileName: string): string;
249
250        /**
251         * Returns a JSON-encoded value of the type:
252         * { textSpan: { start: number, length: number }; hintSpan: { start: number, length: number }; bannerText: string; autoCollapse: boolean } [] = [];
253         */
254        getOutliningSpans(fileName: string): string;
255
256        getTodoComments(fileName: string, todoCommentDescriptors: string): string;
257
258        getBraceMatchingAtPosition(fileName: string, position: number): string;
259        getIndentationAtPosition(fileName: string, position: number, options: string/*Services.EditorOptions*/): string;
260
261        getFormattingEditsForRange(fileName: string, start: number, end: number, options: string/*Services.FormatCodeOptions*/): string;
262        getFormattingEditsForDocument(fileName: string, options: string/*Services.FormatCodeOptions*/): string;
263        getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: string/*Services.FormatCodeOptions*/): string;
264
265        /**
266         * Returns JSON-encoded value of the type TextInsertion.
267         */
268        getDocCommentTemplateAtPosition(fileName: string, position: number, options?: DocCommentTemplateOptions): string;
269
270        /**
271         * Returns JSON-encoded boolean to indicate whether we should support brace location
272         * at the current position.
273         * E.g. we don't want brace completion inside string-literals, comments, etc.
274         */
275        isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): string;
276
277        /**
278         * Returns a JSON-encoded TextSpan | undefined indicating the range of the enclosing comment, if it exists.
279         */
280        getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): string;
281
282        prepareCallHierarchy(fileName: string, position: number): string;
283        provideCallHierarchyIncomingCalls(fileName: string, position: number): string;
284        provideCallHierarchyOutgoingCalls(fileName: string, position: number): string;
285        provideInlayHints(fileName: string, span: TextSpan, preference: UserPreferences | undefined): string;
286        getEmitOutput(fileName: string): string;
287        getEmitOutputObject(fileName: string): EmitOutput;
288
289        toggleLineComment(fileName: string, textChange: TextRange): string;
290        toggleMultilineComment(fileName: string, textChange: TextRange): string;
291        commentSelection(fileName: string, textChange: TextRange): string;
292        uncommentSelection(fileName: string, textChange: TextRange): string;
293    }
294
295    export interface ClassifierShim extends Shim {
296        getEncodedLexicalClassifications(text: string, lexState: EndOfLineState, syntacticClassifierAbsent?: boolean): string;
297        getClassificationsForLine(text: string, lexState: EndOfLineState, syntacticClassifierAbsent?: boolean): string;
298    }
299
300    export interface CoreServicesShim extends Shim {
301        getAutomaticTypeDirectiveNames(compilerOptionsJson: string): string;
302        getPreProcessedFileInfo(fileName: string, sourceText: IScriptSnapshot): string;
303        getTSConfigFileInfo(fileName: string, sourceText: IScriptSnapshot): string;
304        getDefaultCompilationSettings(): string;
305        discoverTypings(discoverTypingsJson: string): string;
306    }
307
308    function logInternalError(logger: Logger, err: Error) {
309        if (logger) {
310            logger.log("*INTERNAL ERROR* - Exception in typescript services: " + err.message);
311        }
312    }
313
314    class ScriptSnapshotShimAdapter implements IScriptSnapshot {
315        constructor(private scriptSnapshotShim: ScriptSnapshotShim) {
316        }
317
318        public getText(start: number, end: number): string {
319            return this.scriptSnapshotShim.getText(start, end);
320        }
321
322        public getLength(): number {
323            return this.scriptSnapshotShim.getLength();
324        }
325
326        public getChangeRange(oldSnapshot: IScriptSnapshot): TextChangeRange | undefined {
327            const oldSnapshotShim = oldSnapshot as ScriptSnapshotShimAdapter;
328            const encoded = this.scriptSnapshotShim.getChangeRange(oldSnapshotShim.scriptSnapshotShim);
329            /* eslint-disable no-null/no-null */
330            if (encoded === null) {
331                return null!; // TODO: GH#18217
332            }
333            /* eslint-enable no-null/no-null */
334
335            const decoded: { span: { start: number; length: number; }; newLength: number; } = JSON.parse(encoded!); // TODO: GH#18217
336            return createTextChangeRange(
337                createTextSpan(decoded.span.start, decoded.span.length), decoded.newLength);
338        }
339
340        public dispose(): void {
341            // if scriptSnapshotShim is a COM object then property check becomes method call with no arguments
342            // 'in' does not have this effect
343            if ("dispose" in this.scriptSnapshotShim) {
344                this.scriptSnapshotShim.dispose!(); // TODO: GH#18217 Can we just use `if (this.scriptSnapshotShim.dispose)`?
345            }
346        }
347    }
348
349    export class LanguageServiceShimHostAdapter implements LanguageServiceHost {
350        private loggingEnabled = false;
351        private tracingEnabled = false;
352
353        public resolveModuleNames: ((moduleName: string[], containingFile: string) => (ResolvedModuleFull | undefined)[]) | undefined;
354        public resolveTypeReferenceDirectives: ((typeDirectiveNames: string[] | readonly FileReference[], containingFile: string) => (ResolvedTypeReferenceDirective | undefined)[]) | undefined;
355        public directoryExists: ((directoryName: string) => boolean) | undefined;
356
357        constructor(private shimHost: LanguageServiceShimHost) {
358            // if shimHost is a COM object then property check will become method call with no arguments.
359            // 'in' does not have this effect.
360            if ("getModuleResolutionsForFile" in this.shimHost) {
361                this.resolveModuleNames = (moduleNames, containingFile) => {
362                    const resolutionsInFile = JSON.parse(this.shimHost.getModuleResolutionsForFile!(containingFile)) as MapLike<string>; // TODO: GH#18217
363                    return map(moduleNames, name => {
364                        const result = getProperty(resolutionsInFile, name);
365                        return result ? { resolvedFileName: result, extension: extensionFromPath(result), isExternalLibraryImport: false } : undefined;
366                    });
367                };
368            }
369            if ("directoryExists" in this.shimHost) {
370                this.directoryExists = directoryName => this.shimHost.directoryExists(directoryName);
371            }
372            if ("getTypeReferenceDirectiveResolutionsForFile" in this.shimHost) {
373                this.resolveTypeReferenceDirectives = (typeDirectiveNames, containingFile) => {
374                    const typeDirectivesForFile = JSON.parse(this.shimHost.getTypeReferenceDirectiveResolutionsForFile!(containingFile)) as MapLike<ResolvedTypeReferenceDirective>; // TODO: GH#18217
375                    return map(typeDirectiveNames as (string | FileReference)[], name => getProperty(typeDirectivesForFile, isString(name) ? name : name.fileName.toLowerCase()));
376                };
377            }
378        }
379
380        public log(s: string): void {
381            if (this.loggingEnabled) {
382                this.shimHost.log(s);
383            }
384        }
385
386        public trace(s: string): void {
387            if (this.tracingEnabled) {
388                this.shimHost.trace(s);
389            }
390        }
391
392        public error(s: string): void {
393            this.shimHost.error(s);
394        }
395
396        public getProjectVersion(): string {
397            if (!this.shimHost.getProjectVersion) {
398                // shimmed host does not support getProjectVersion
399                return undefined!; // TODO: GH#18217
400            }
401
402            return this.shimHost.getProjectVersion();
403        }
404
405        public getTypeRootsVersion(): number {
406            if (!this.shimHost.getTypeRootsVersion) {
407                return 0;
408            }
409            return this.shimHost.getTypeRootsVersion();
410        }
411
412        public useCaseSensitiveFileNames(): boolean {
413            return this.shimHost.useCaseSensitiveFileNames ? this.shimHost.useCaseSensitiveFileNames() : false;
414        }
415
416        public getCompilationSettings(): CompilerOptions {
417            const settingsJson = this.shimHost.getCompilationSettings();
418            // eslint-disable-next-line no-null/no-null
419            if (settingsJson === null || settingsJson === "") {
420                throw Error("LanguageServiceShimHostAdapter.getCompilationSettings: empty compilationSettings");
421            }
422            const compilerOptions = JSON.parse(settingsJson) as CompilerOptions;
423            // permit language service to handle all files (filtering should be performed on the host side)
424            compilerOptions.allowNonTsExtensions = true;
425            return compilerOptions;
426        }
427
428        public getScriptFileNames(): string[] {
429            const encoded = this.shimHost.getScriptFileNames();
430            return JSON.parse(encoded);
431        }
432
433        public getScriptSnapshot(fileName: string): IScriptSnapshot | undefined {
434            const scriptSnapshot = this.shimHost.getScriptSnapshot(fileName);
435            return scriptSnapshot && new ScriptSnapshotShimAdapter(scriptSnapshot);
436        }
437
438        public getScriptKind(fileName: string): ScriptKind {
439            if ("getScriptKind" in this.shimHost) {
440                return this.shimHost.getScriptKind!(fileName); // TODO: GH#18217
441            }
442            else {
443                return ScriptKind.Unknown;
444            }
445        }
446
447        public getScriptVersion(fileName: string): string {
448            return this.shimHost.getScriptVersion(fileName);
449        }
450
451        public getLocalizedDiagnosticMessages() {
452            /* eslint-disable no-null/no-null */
453            const diagnosticMessagesJson = this.shimHost.getLocalizedDiagnosticMessages();
454            if (diagnosticMessagesJson === null || diagnosticMessagesJson === "") {
455                return null;
456            }
457
458            try {
459                return JSON.parse(diagnosticMessagesJson);
460            }
461            catch (e) {
462                this.log(e.description || "diagnosticMessages.generated.json has invalid JSON format");
463                return null;
464            }
465            /* eslint-enable no-null/no-null */
466        }
467
468        public getCancellationToken(): HostCancellationToken {
469            const hostCancellationToken = this.shimHost.getCancellationToken();
470            return new ThrottledCancellationToken(hostCancellationToken);
471        }
472
473        public getCurrentDirectory(): string {
474            return this.shimHost.getCurrentDirectory();
475        }
476
477        public getDirectories(path: string): string[] {
478            return JSON.parse(this.shimHost.getDirectories(path));
479        }
480
481        public getDefaultLibFileName(options: CompilerOptions): string {
482            return this.shimHost.getDefaultLibFileName(JSON.stringify(options));
483        }
484
485        public readDirectory(path: string, extensions?: readonly string[], exclude?: string[], include?: string[], depth?: number): string[] {
486            const pattern = getFileMatcherPatterns(path, exclude, include,
487                this.shimHost.useCaseSensitiveFileNames!(), this.shimHost.getCurrentDirectory()); // TODO: GH#18217
488            return JSON.parse(this.shimHost.readDirectory(
489                path,
490                JSON.stringify(extensions),
491                JSON.stringify(pattern.basePaths),
492                pattern.excludePattern,
493                pattern.includeFilePattern,
494                pattern.includeDirectoryPattern,
495                depth
496            ));
497        }
498
499        public readFile(path: string, encoding?: string): string | undefined {
500            return this.shimHost.readFile(path, encoding);
501        }
502
503        public fileExists(path: string): boolean {
504            return this.shimHost.fileExists(path);
505        }
506    }
507
508    export class CoreServicesShimHostAdapter implements ParseConfigHost, ModuleResolutionHost, JsTyping.TypingResolutionHost {
509
510        public directoryExists: (directoryName: string) => boolean;
511        public realpath: (path: string) => string;
512        public useCaseSensitiveFileNames: boolean;
513
514        constructor(private shimHost: CoreServicesShimHost) {
515            this.useCaseSensitiveFileNames = this.shimHost.useCaseSensitiveFileNames ? this.shimHost.useCaseSensitiveFileNames() : false;
516            if ("directoryExists" in this.shimHost) {
517                this.directoryExists = directoryName => this.shimHost.directoryExists(directoryName);
518            }
519            else {
520                this.directoryExists = undefined!; // TODO: GH#18217
521            }
522            if ("realpath" in this.shimHost) {
523                this.realpath = path => this.shimHost.realpath!(path); // TODO: GH#18217
524            }
525            else {
526                this.realpath = undefined!; // TODO: GH#18217
527            }
528        }
529
530        public readDirectory(rootDir: string, extensions: readonly string[], exclude: readonly string[], include: readonly string[], depth?: number): string[] {
531            const pattern = getFileMatcherPatterns(rootDir, exclude, include,
532                this.shimHost.useCaseSensitiveFileNames!(), this.shimHost.getCurrentDirectory()); // TODO: GH#18217
533            return JSON.parse(this.shimHost.readDirectory(
534                rootDir,
535                JSON.stringify(extensions),
536                JSON.stringify(pattern.basePaths),
537                pattern.excludePattern,
538                pattern.includeFilePattern,
539                pattern.includeDirectoryPattern,
540                depth
541            ));
542        }
543
544        public fileExists(fileName: string): boolean {
545            return this.shimHost.fileExists(fileName);
546        }
547
548        public readFile(fileName: string): string | undefined {
549            return this.shimHost.readFile(fileName);
550        }
551
552        public getDirectories(path: string): string[] {
553            return JSON.parse(this.shimHost.getDirectories(path));
554        }
555    }
556
557    function simpleForwardCall(logger: Logger, actionDescription: string, action: () => unknown, logPerformance: boolean): unknown {
558        let start: number | undefined;
559        if (logPerformance) {
560            logger.log(actionDescription);
561            start = timestamp();
562        }
563
564        const result = action();
565
566        if (logPerformance) {
567            const end = timestamp();
568            logger.log(`${actionDescription} completed in ${end - start!} msec`);
569            if (isString(result)) {
570                let str = result;
571                if (str.length > 128) {
572                    str = str.substring(0, 128) + "...";
573                }
574                logger.log(`  result.length=${str.length}, result='${JSON.stringify(str)}'`);
575            }
576        }
577
578        return result;
579    }
580
581    function forwardJSONCall(logger: Logger, actionDescription: string, action: () => {} | null | undefined, logPerformance: boolean): string {
582        return forwardCall(logger, actionDescription, /*returnJson*/ true, action, logPerformance) as string;
583    }
584
585    function forwardCall<T>(logger: Logger, actionDescription: string, returnJson: boolean, action: () => T, logPerformance: boolean): T | string {
586        try {
587            const result = simpleForwardCall(logger, actionDescription, action, logPerformance);
588            return returnJson ? JSON.stringify({ result }) : result as T;
589        }
590        catch (err) {
591            if (err instanceof OperationCanceledException) {
592                return JSON.stringify({ canceled: true });
593            }
594            logInternalError(logger, err);
595            err.description = actionDescription;
596            return JSON.stringify({ error: err });
597        }
598    }
599
600
601    class ShimBase implements Shim {
602        constructor(private factory: ShimFactory) {
603            factory.registerShim(this);
604        }
605        public dispose(_dummy: {}): void {
606            this.factory.unregisterShim(this);
607        }
608    }
609
610    export interface RealizedDiagnostic {
611        message: string;
612        start: number;
613        length: number;
614        category: string;
615        code: number;
616        reportsUnnecessary?: {};
617        reportsDeprecated?: {};
618    }
619    export function realizeDiagnostics(diagnostics: readonly Diagnostic[], newLine: string): RealizedDiagnostic[] {
620        return diagnostics.map(d => realizeDiagnostic(d, newLine));
621    }
622
623    function realizeDiagnostic(diagnostic: Diagnostic, newLine: string): RealizedDiagnostic {
624        return {
625            message: flattenDiagnosticMessageText(diagnostic.messageText, newLine),
626            start: diagnostic.start!, // TODO: GH#18217
627            length: diagnostic.length!, // TODO: GH#18217
628            category: diagnosticCategoryName(diagnostic),
629            code: diagnostic.code,
630            reportsUnnecessary: diagnostic.reportsUnnecessary,
631            reportsDeprecated: diagnostic.reportsDeprecated
632        };
633    }
634
635    class LanguageServiceShimObject extends ShimBase implements LanguageServiceShim {
636        private logger: Logger;
637        private logPerformance = false;
638
639        constructor(factory: ShimFactory,
640            private host: LanguageServiceShimHost,
641            public languageService: LanguageService) {
642            super(factory);
643            this.logger = this.host;
644        }
645
646        public forwardJSONCall(actionDescription: string, action: () => {} | null | undefined): string {
647            return forwardJSONCall(this.logger, actionDescription, action, this.logPerformance);
648        }
649
650        /// DISPOSE
651
652        /**
653         * Ensure (almost) deterministic release of internal Javascript resources when
654         * some external native objects holds onto us (e.g. Com/Interop).
655         */
656        public dispose(dummy: {}): void {
657            this.logger.log("dispose()");
658            this.languageService.dispose();
659            this.languageService = null!; // eslint-disable-line no-null/no-null
660
661            // force a GC
662            if (debugObjectHost && debugObjectHost.CollectGarbage) {
663                debugObjectHost.CollectGarbage();
664                this.logger.log("CollectGarbage()");
665            }
666
667            this.logger = null!; // eslint-disable-line no-null/no-null
668
669            super.dispose(dummy);
670        }
671
672        /// REFRESH
673
674        /**
675         * Update the list of scripts known to the compiler
676         */
677        public refresh(throwOnError: boolean): void {
678            this.forwardJSONCall(
679                `refresh(${throwOnError})`,
680                () => null // eslint-disable-line no-null/no-null
681            );
682        }
683
684        public cleanupSemanticCache(): void {
685            this.forwardJSONCall(
686                "cleanupSemanticCache()",
687                () => {
688                    this.languageService.cleanupSemanticCache();
689                    return null; // eslint-disable-line no-null/no-null
690                });
691        }
692
693        private realizeDiagnostics(diagnostics: readonly Diagnostic[]): { message: string; start: number; length: number; category: string; }[] {
694            const newLine = getNewLineOrDefaultFromHost(this.host);
695            return realizeDiagnostics(diagnostics, newLine);
696        }
697
698        public getSyntacticClassifications(fileName: string, start: number, length: number): string {
699            return this.forwardJSONCall(
700                `getSyntacticClassifications('${fileName}', ${start}, ${length})`,
701                () => this.languageService.getSyntacticClassifications(fileName, createTextSpan(start, length))
702            );
703        }
704
705        public getSemanticClassifications(fileName: string, start: number, length: number): string {
706            return this.forwardJSONCall(
707                `getSemanticClassifications('${fileName}', ${start}, ${length})`,
708                () => this.languageService.getSemanticClassifications(fileName, createTextSpan(start, length))
709            );
710        }
711
712        public getEncodedSyntacticClassifications(fileName: string, start: number, length: number): string {
713            return this.forwardJSONCall(
714                `getEncodedSyntacticClassifications('${fileName}', ${start}, ${length})`,
715                // directly serialize the spans out to a string.  This is much faster to decode
716                // on the managed side versus a full JSON array.
717                () => convertClassifications(this.languageService.getEncodedSyntacticClassifications(fileName, createTextSpan(start, length)))
718            );
719        }
720
721        public getEncodedSemanticClassifications(fileName: string, start: number, length: number): string {
722            return this.forwardJSONCall(
723                `getEncodedSemanticClassifications('${fileName}', ${start}, ${length})`,
724                // directly serialize the spans out to a string.  This is much faster to decode
725                // on the managed side versus a full JSON array.
726                () => convertClassifications(this.languageService.getEncodedSemanticClassifications(fileName, createTextSpan(start, length)))
727            );
728        }
729
730        public getSyntacticDiagnostics(fileName: string): string {
731            return this.forwardJSONCall(
732                `getSyntacticDiagnostics('${fileName}')`,
733                () => {
734                    const diagnostics = this.languageService.getSyntacticDiagnostics(fileName);
735                    return this.realizeDiagnostics(diagnostics);
736                });
737        }
738
739        public getSemanticDiagnostics(fileName: string): string {
740            return this.forwardJSONCall(
741                `getSemanticDiagnostics('${fileName}')`,
742                () => {
743                    const diagnostics = this.languageService.getSemanticDiagnostics(fileName);
744                    return this.realizeDiagnostics(diagnostics);
745                });
746        }
747
748        public getSuggestionDiagnostics(fileName: string): string {
749            return this.forwardJSONCall(`getSuggestionDiagnostics('${fileName}')`, () => this.realizeDiagnostics(this.languageService.getSuggestionDiagnostics(fileName)));
750        }
751
752        public getCompilerOptionsDiagnostics(): string {
753            return this.forwardJSONCall(
754                "getCompilerOptionsDiagnostics()",
755                () => {
756                    const diagnostics = this.languageService.getCompilerOptionsDiagnostics();
757                    return this.realizeDiagnostics(diagnostics);
758                });
759        }
760
761        /// QUICKINFO
762
763        /**
764         * Computes a string representation of the type at the requested position
765         * in the active file.
766         */
767        public getQuickInfoAtPosition(fileName: string, position: number): string {
768            return this.forwardJSONCall(
769                `getQuickInfoAtPosition('${fileName}', ${position})`,
770                () => this.languageService.getQuickInfoAtPosition(fileName, position)
771            );
772        }
773
774
775        /// NAMEORDOTTEDNAMESPAN
776
777        /**
778         * Computes span information of the name or dotted name at the requested position
779         * in the active file.
780         */
781        public getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): string {
782            return this.forwardJSONCall(
783                `getNameOrDottedNameSpan('${fileName}', ${startPos}, ${endPos})`,
784                () => this.languageService.getNameOrDottedNameSpan(fileName, startPos, endPos)
785            );
786        }
787
788        /**
789         * STATEMENTSPAN
790         * Computes span information of statement at the requested position in the active file.
791         */
792        public getBreakpointStatementAtPosition(fileName: string, position: number): string {
793            return this.forwardJSONCall(
794                `getBreakpointStatementAtPosition('${fileName}', ${position})`,
795                () => this.languageService.getBreakpointStatementAtPosition(fileName, position)
796            );
797        }
798
799        /// SIGNATUREHELP
800
801        public getSignatureHelpItems(fileName: string, position: number, options: SignatureHelpItemsOptions | undefined): string {
802            return this.forwardJSONCall(
803                `getSignatureHelpItems('${fileName}', ${position})`,
804                () => this.languageService.getSignatureHelpItems(fileName, position, options)
805            );
806        }
807
808        /// GOTO DEFINITION
809
810        /**
811         * Computes the definition location and file for the symbol
812         * at the requested position.
813         */
814        public getDefinitionAtPosition(fileName: string, position: number): string {
815            return this.forwardJSONCall(
816                `getDefinitionAtPosition('${fileName}', ${position})`,
817                () => this.languageService.getDefinitionAtPosition(fileName, position)
818            );
819        }
820
821        /**
822         * Computes the definition location and file for the symbol
823         * at the requested position.
824         */
825        public getDefinitionAndBoundSpan(fileName: string, position: number): string {
826            return this.forwardJSONCall(
827                `getDefinitionAndBoundSpan('${fileName}', ${position})`,
828                () => this.languageService.getDefinitionAndBoundSpan(fileName, position)
829            );
830        }
831
832        /// GOTO Type
833
834        /**
835         * Computes the definition location of the type of the symbol
836         * at the requested position.
837         */
838        public getTypeDefinitionAtPosition(fileName: string, position: number): string {
839            return this.forwardJSONCall(
840                `getTypeDefinitionAtPosition('${fileName}', ${position})`,
841                () => this.languageService.getTypeDefinitionAtPosition(fileName, position)
842            );
843        }
844
845        /// GOTO Implementation
846
847        /**
848         * Computes the implementation location of the symbol
849         * at the requested position.
850         */
851        public getImplementationAtPosition(fileName: string, position: number): string {
852            return this.forwardJSONCall(
853                `getImplementationAtPosition('${fileName}', ${position})`,
854                () => this.languageService.getImplementationAtPosition(fileName, position)
855            );
856        }
857
858        public getRenameInfo(fileName: string, position: number, preferences: UserPreferences): string {
859            return this.forwardJSONCall(
860                `getRenameInfo('${fileName}', ${position})`,
861                () => this.languageService.getRenameInfo(fileName, position, preferences)
862            );
863        }
864
865        public getSmartSelectionRange(fileName: string, position: number): string {
866            return this.forwardJSONCall(
867                `getSmartSelectionRange('${fileName}', ${position})`,
868                () => this.languageService.getSmartSelectionRange(fileName, position)
869            );
870        }
871
872        public findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): string {
873            return this.forwardJSONCall(
874                `findRenameLocations('${fileName}', ${position}, ${findInStrings}, ${findInComments}, ${providePrefixAndSuffixTextForRename})`,
875                () => this.languageService.findRenameLocations(fileName, position, findInStrings, findInComments, providePrefixAndSuffixTextForRename)
876            );
877        }
878
879        /// GET BRACE MATCHING
880        public getBraceMatchingAtPosition(fileName: string, position: number): string {
881            return this.forwardJSONCall(
882                `getBraceMatchingAtPosition('${fileName}', ${position})`,
883                () => this.languageService.getBraceMatchingAtPosition(fileName, position)
884            );
885        }
886
887        public isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): string {
888            return this.forwardJSONCall(
889                `isValidBraceCompletionAtPosition('${fileName}', ${position}, ${openingBrace})`,
890                () => this.languageService.isValidBraceCompletionAtPosition(fileName, position, openingBrace)
891            );
892        }
893
894        public getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): string {
895            return this.forwardJSONCall(
896                `getSpanOfEnclosingComment('${fileName}', ${position})`,
897                () => this.languageService.getSpanOfEnclosingComment(fileName, position, onlyMultiLine)
898            );
899        }
900
901        /// GET SMART INDENT
902        public getIndentationAtPosition(fileName: string, position: number, options: string /*Services.EditorOptions*/): string {
903            return this.forwardJSONCall(
904                `getIndentationAtPosition('${fileName}', ${position})`,
905                () => {
906                    const localOptions: EditorOptions = JSON.parse(options);
907                    return this.languageService.getIndentationAtPosition(fileName, position, localOptions);
908                });
909        }
910
911        /// GET REFERENCES
912
913        public getReferencesAtPosition(fileName: string, position: number): string {
914            return this.forwardJSONCall(
915                `getReferencesAtPosition('${fileName}', ${position})`,
916                () => this.languageService.getReferencesAtPosition(fileName, position)
917            );
918        }
919
920        public findReferences(fileName: string, position: number): string {
921            return this.forwardJSONCall(
922                `findReferences('${fileName}', ${position})`,
923                () => this.languageService.findReferences(fileName, position)
924            );
925        }
926
927        public getFileReferences(fileName: string) {
928            return this.forwardJSONCall(
929                `getFileReferences('${fileName})`,
930                () => this.languageService.getFileReferences(fileName)
931            );
932        }
933
934        public getOccurrencesAtPosition(fileName: string, position: number): string {
935            return this.forwardJSONCall(
936                `getOccurrencesAtPosition('${fileName}', ${position})`,
937                () => this.languageService.getOccurrencesAtPosition(fileName, position)
938            );
939        }
940
941        public getDocumentHighlights(fileName: string, position: number, filesToSearch: string): string {
942            return this.forwardJSONCall(
943                `getDocumentHighlights('${fileName}', ${position})`,
944                () => {
945                    const results = this.languageService.getDocumentHighlights(fileName, position, JSON.parse(filesToSearch));
946                    // workaround for VS document highlighting issue - keep only items from the initial file
947                    const normalizedName = toFileNameLowerCase(normalizeSlashes(fileName));
948                    return filter(results, r => toFileNameLowerCase(normalizeSlashes(r.fileName)) === normalizedName);
949                });
950        }
951
952        /// COMPLETION LISTS
953
954        /**
955         * Get a string based representation of the completions
956         * to provide at the given source position and providing a member completion
957         * list if requested.
958         */
959        public getCompletionsAtPosition(fileName: string, position: number, preferences: GetCompletionsAtPositionOptions | undefined, formattingSettings: FormatCodeSettings | undefined) {
960            return this.forwardJSONCall(
961                `getCompletionsAtPosition('${fileName}', ${position}, ${preferences}, ${formattingSettings})`,
962                () => this.languageService.getCompletionsAtPosition(fileName, position, preferences, formattingSettings)
963            );
964        }
965
966        /** Get a string based representation of a completion list entry details */
967        public getCompletionEntryDetails(fileName: string, position: number, entryName: string, formatOptions: string/*Services.FormatCodeOptions*/ | undefined, source: string | undefined, preferences: UserPreferences | undefined, data: CompletionEntryData | undefined) {
968            return this.forwardJSONCall(
969                `getCompletionEntryDetails('${fileName}', ${position}, '${entryName}')`,
970                () => {
971                    const localOptions: FormatCodeOptions = formatOptions === undefined ? undefined : JSON.parse(formatOptions);
972                    return this.languageService.getCompletionEntryDetails(fileName, position, entryName, localOptions, source, preferences, data);
973                }
974            );
975        }
976
977        public getFormattingEditsForRange(fileName: string, start: number, end: number, options: string/*Services.FormatCodeOptions*/): string {
978            return this.forwardJSONCall(
979                `getFormattingEditsForRange('${fileName}', ${start}, ${end})`,
980                () => {
981                    const localOptions: FormatCodeOptions = JSON.parse(options);
982                    return this.languageService.getFormattingEditsForRange(fileName, start, end, localOptions);
983                });
984        }
985
986        public getFormattingEditsForDocument(fileName: string, options: string/*Services.FormatCodeOptions*/): string {
987            return this.forwardJSONCall(
988                `getFormattingEditsForDocument('${fileName}')`,
989                () => {
990                    const localOptions: FormatCodeOptions = JSON.parse(options);
991                    return this.languageService.getFormattingEditsForDocument(fileName, localOptions);
992                });
993        }
994
995        public getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: string/*Services.FormatCodeOptions*/): string {
996            return this.forwardJSONCall(
997                `getFormattingEditsAfterKeystroke('${fileName}', ${position}, '${key}')`,
998                () => {
999                    const localOptions: FormatCodeOptions = JSON.parse(options);
1000                    return this.languageService.getFormattingEditsAfterKeystroke(fileName, position, key, localOptions);
1001                });
1002        }
1003
1004        public getDocCommentTemplateAtPosition(fileName: string, position: number, options?: DocCommentTemplateOptions): string {
1005            return this.forwardJSONCall(
1006                `getDocCommentTemplateAtPosition('${fileName}', ${position})`,
1007                () => this.languageService.getDocCommentTemplateAtPosition(fileName, position, options)
1008            );
1009        }
1010
1011        /// NAVIGATE TO
1012
1013        /** Return a list of symbols that are interesting to navigate to */
1014        public getNavigateToItems(searchValue: string, maxResultCount?: number, fileName?: string): string {
1015            return this.forwardJSONCall(
1016                `getNavigateToItems('${searchValue}', ${maxResultCount}, ${fileName})`,
1017                () => this.languageService.getNavigateToItems(searchValue, maxResultCount, fileName)
1018            );
1019        }
1020
1021        public getNavigationBarItems(fileName: string): string {
1022            return this.forwardJSONCall(
1023                `getNavigationBarItems('${fileName}')`,
1024                () => this.languageService.getNavigationBarItems(fileName)
1025            );
1026        }
1027
1028        public getNavigationTree(fileName: string): string {
1029            return this.forwardJSONCall(
1030                `getNavigationTree('${fileName}')`,
1031                () => this.languageService.getNavigationTree(fileName)
1032            );
1033        }
1034
1035        public getOutliningSpans(fileName: string): string {
1036            return this.forwardJSONCall(
1037                `getOutliningSpans('${fileName}')`,
1038                () => this.languageService.getOutliningSpans(fileName)
1039            );
1040        }
1041
1042        public getTodoComments(fileName: string, descriptors: string): string {
1043            return this.forwardJSONCall(
1044                `getTodoComments('${fileName}')`,
1045                () => this.languageService.getTodoComments(fileName, JSON.parse(descriptors))
1046            );
1047        }
1048
1049        /// CALL HIERARCHY
1050
1051        public prepareCallHierarchy(fileName: string, position: number): string {
1052            return this.forwardJSONCall(
1053                `prepareCallHierarchy('${fileName}', ${position})`,
1054                () => this.languageService.prepareCallHierarchy(fileName, position)
1055            );
1056        }
1057
1058        public provideCallHierarchyIncomingCalls(fileName: string, position: number): string {
1059            return this.forwardJSONCall(
1060                `provideCallHierarchyIncomingCalls('${fileName}', ${position})`,
1061                () => this.languageService.provideCallHierarchyIncomingCalls(fileName, position)
1062            );
1063        }
1064
1065        public provideCallHierarchyOutgoingCalls(fileName: string, position: number): string {
1066            return this.forwardJSONCall(
1067                `provideCallHierarchyOutgoingCalls('${fileName}', ${position})`,
1068                () => this.languageService.provideCallHierarchyOutgoingCalls(fileName, position)
1069            );
1070        }
1071
1072        public provideInlayHints(fileName: string, span: TextSpan, preference: UserPreferences | undefined): string {
1073            return this.forwardJSONCall(
1074                `provideInlayHints('${fileName}', '${JSON.stringify(span)}', ${JSON.stringify(preference)})`,
1075                () => this.languageService.provideInlayHints(fileName, span, preference)
1076            );
1077        }
1078
1079        /// Emit
1080        public getEmitOutput(fileName: string): string {
1081            return this.forwardJSONCall(
1082                `getEmitOutput('${fileName}')`,
1083                () => {
1084                    const { diagnostics, ...rest } = this.languageService.getEmitOutput(fileName);
1085                    return { ...rest, diagnostics: this.realizeDiagnostics(diagnostics) };
1086                }
1087            );
1088        }
1089
1090        public getEmitOutputObject(fileName: string): EmitOutput {
1091            return forwardCall(
1092                this.logger,
1093                `getEmitOutput('${fileName}')`,
1094                /*returnJson*/ false,
1095                () => this.languageService.getEmitOutput(fileName),
1096                this.logPerformance) as EmitOutput;
1097        }
1098
1099        public toggleLineComment(fileName: string, textRange: TextRange): string {
1100            return this.forwardJSONCall(
1101                `toggleLineComment('${fileName}', '${JSON.stringify(textRange)}')`,
1102                () => this.languageService.toggleLineComment(fileName, textRange)
1103            );
1104        }
1105
1106        public toggleMultilineComment(fileName: string, textRange: TextRange): string {
1107            return this.forwardJSONCall(
1108                `toggleMultilineComment('${fileName}', '${JSON.stringify(textRange)}')`,
1109                () => this.languageService.toggleMultilineComment(fileName, textRange)
1110            );
1111        }
1112
1113        public commentSelection(fileName: string, textRange: TextRange): string {
1114            return this.forwardJSONCall(
1115                `commentSelection('${fileName}', '${JSON.stringify(textRange)}')`,
1116                () => this.languageService.commentSelection(fileName, textRange)
1117            );
1118        }
1119
1120        public uncommentSelection(fileName: string, textRange: TextRange): string {
1121            return this.forwardJSONCall(
1122                `uncommentSelection('${fileName}', '${JSON.stringify(textRange)}')`,
1123                () => this.languageService.uncommentSelection(fileName, textRange)
1124            );
1125        }
1126    }
1127
1128    function convertClassifications(classifications: Classifications): { spans: string, endOfLineState: EndOfLineState } {
1129        return { spans: classifications.spans.join(","), endOfLineState: classifications.endOfLineState };
1130    }
1131
1132    class ClassifierShimObject extends ShimBase implements ClassifierShim {
1133        public classifier: Classifier;
1134        private logPerformance = false;
1135
1136        constructor(factory: ShimFactory, private logger: Logger) {
1137            super(factory);
1138            this.classifier = createClassifier();
1139        }
1140
1141        public getEncodedLexicalClassifications(text: string, lexState: EndOfLineState, syntacticClassifierAbsent = false): string {
1142            return forwardJSONCall(this.logger, "getEncodedLexicalClassifications",
1143                () => convertClassifications(this.classifier.getEncodedLexicalClassifications(text, lexState, syntacticClassifierAbsent)),
1144                this.logPerformance);
1145        }
1146
1147        /// COLORIZATION
1148        public getClassificationsForLine(text: string, lexState: EndOfLineState, classifyKeywordsInGenerics = false): string {
1149            const classification = this.classifier.getClassificationsForLine(text, lexState, classifyKeywordsInGenerics);
1150            let result = "";
1151            for (const item of classification.entries) {
1152                result += item.length + "\n";
1153                result += item.classification + "\n";
1154            }
1155            result += classification.finalLexState;
1156            return result;
1157        }
1158    }
1159
1160    class CoreServicesShimObject extends ShimBase implements CoreServicesShim {
1161        private logPerformance = false;
1162        private safeList: JsTyping.SafeList | undefined;
1163
1164        constructor(factory: ShimFactory, public readonly logger: Logger, private readonly host: CoreServicesShimHostAdapter) {
1165            super(factory);
1166        }
1167
1168        private forwardJSONCall(actionDescription: string, action: () => {}): string {
1169            return forwardJSONCall(this.logger, actionDescription, action, this.logPerformance);
1170        }
1171
1172        public resolveModuleName(fileName: string, moduleName: string, compilerOptionsJson: string): string {
1173            return this.forwardJSONCall(`resolveModuleName('${fileName}')`, () => {
1174                const compilerOptions = JSON.parse(compilerOptionsJson) as CompilerOptions;
1175                const result = resolveModuleName(moduleName, normalizeSlashes(fileName), compilerOptions, this.host);
1176                let resolvedFileName = result.resolvedModule ? result.resolvedModule.resolvedFileName : undefined;
1177                if (result.resolvedModule && result.resolvedModule.extension !== Extension.Ts && result.resolvedModule.extension !== Extension.Tsx && result.resolvedModule.extension !== Extension.Dts &&
1178                    result.resolvedModule.extension !== Extension.Ets && result.resolvedModule.extension !== Extension.Dets) {
1179                    resolvedFileName = undefined;
1180                }
1181
1182                return {
1183                    resolvedFileName,
1184                    failedLookupLocations: result.failedLookupLocations,
1185                    affectingLocations: result.affectingLocations,
1186                };
1187            });
1188        }
1189
1190        public resolveTypeReferenceDirective(fileName: string, typeReferenceDirective: string, compilerOptionsJson: string): string {
1191            return this.forwardJSONCall(`resolveTypeReferenceDirective(${fileName})`, () => {
1192                const compilerOptions = JSON.parse(compilerOptionsJson) as CompilerOptions;
1193                const result = resolveTypeReferenceDirective(typeReferenceDirective, normalizeSlashes(fileName), compilerOptions, this.host);
1194                return {
1195                    resolvedFileName: result.resolvedTypeReferenceDirective ? result.resolvedTypeReferenceDirective.resolvedFileName : undefined,
1196                    primary: result.resolvedTypeReferenceDirective ? result.resolvedTypeReferenceDirective.primary : true,
1197                    failedLookupLocations: result.failedLookupLocations
1198                };
1199            });
1200        }
1201
1202        public getPreProcessedFileInfo(fileName: string, sourceTextSnapshot: IScriptSnapshot): string {
1203            return this.forwardJSONCall(
1204                `getPreProcessedFileInfo('${fileName}')`,
1205                () => {
1206                    // for now treat files as JavaScript
1207                    const result = preProcessFile(getSnapshotText(sourceTextSnapshot), /* readImportFiles */ true, /* detectJavaScriptImports */ true);
1208                    return {
1209                        referencedFiles: this.convertFileReferences(result.referencedFiles),
1210                        importedFiles: this.convertFileReferences(result.importedFiles),
1211                        ambientExternalModules: result.ambientExternalModules,
1212                        isLibFile: result.isLibFile,
1213                        typeReferenceDirectives: this.convertFileReferences(result.typeReferenceDirectives),
1214                        libReferenceDirectives: this.convertFileReferences(result.libReferenceDirectives)
1215                    };
1216                });
1217        }
1218
1219        public getAutomaticTypeDirectiveNames(compilerOptionsJson: string): string {
1220            return this.forwardJSONCall(
1221                `getAutomaticTypeDirectiveNames('${compilerOptionsJson}')`,
1222                () => {
1223                    const compilerOptions = JSON.parse(compilerOptionsJson) as CompilerOptions;
1224                    return getAutomaticTypeDirectiveNames(compilerOptions, this.host);
1225                }
1226            );
1227        }
1228
1229        private convertFileReferences(refs: FileReference[]): ShimsFileReference[] | undefined {
1230            if (!refs) {
1231                return undefined;
1232            }
1233            const result: ShimsFileReference[] = [];
1234            for (const ref of refs) {
1235                result.push({
1236                    path: normalizeSlashes(ref.fileName),
1237                    position: ref.pos,
1238                    length: ref.end - ref.pos
1239                });
1240            }
1241            return result;
1242        }
1243
1244        public getTSConfigFileInfo(fileName: string, sourceTextSnapshot: IScriptSnapshot): string {
1245            return this.forwardJSONCall(
1246                `getTSConfigFileInfo('${fileName}')`,
1247                () => {
1248                    const result = parseJsonText(fileName, getSnapshotText(sourceTextSnapshot));
1249                    const normalizedFileName = normalizeSlashes(fileName);
1250                    const configFile = parseJsonSourceFileConfigFileContent(result, this.host, getDirectoryPath(normalizedFileName), /*existingOptions*/ {}, normalizedFileName);
1251
1252                    return {
1253                        options: configFile.options,
1254                        typeAcquisition: configFile.typeAcquisition,
1255                        files: configFile.fileNames,
1256                        raw: configFile.raw,
1257                        errors: realizeDiagnostics([...result.parseDiagnostics, ...configFile.errors], "\r\n")
1258                    };
1259                });
1260        }
1261
1262        public getDefaultCompilationSettings(): string {
1263            return this.forwardJSONCall(
1264                "getDefaultCompilationSettings()",
1265                () => getDefaultCompilerOptions()
1266            );
1267        }
1268
1269        public discoverTypings(discoverTypingsJson: string): string {
1270            const getCanonicalFileName = createGetCanonicalFileName(/*useCaseSensitivefileNames:*/ false);
1271            return this.forwardJSONCall("discoverTypings()", () => {
1272                const info = JSON.parse(discoverTypingsJson) as DiscoverTypingsInfo;
1273                if (this.safeList === undefined) {
1274                    this.safeList = JsTyping.loadSafeList(this.host, toPath(info.safeListPath, info.safeListPath, getCanonicalFileName));
1275                }
1276                return JsTyping.discoverTypings(
1277                    this.host,
1278                    msg => this.logger.log(msg),
1279                    info.fileNames,
1280                    toPath(info.projectRootPath, info.projectRootPath, getCanonicalFileName),
1281                    this.safeList,
1282                    info.packageNameToTypingLocation,
1283                    info.typeAcquisition,
1284                    info.unresolvedImports,
1285                    info.typesRegistry,
1286                    emptyOptions);
1287            });
1288        }
1289    }
1290
1291    export class TypeScriptServicesFactory implements ShimFactory {
1292        private _shims: Shim[] = [];
1293        private documentRegistry: DocumentRegistry | undefined;
1294
1295        /*
1296         * Returns script API version.
1297         */
1298        public getServicesVersion(): string {
1299            return servicesVersion;
1300        }
1301
1302        public createLanguageServiceShim(host: LanguageServiceShimHost): LanguageServiceShim {
1303            try {
1304                if (this.documentRegistry === undefined) {
1305                    this.documentRegistry = createDocumentRegistry(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames(), host.getCurrentDirectory());
1306                }
1307                const hostAdapter = new LanguageServiceShimHostAdapter(host);
1308                const languageService = createLanguageService(hostAdapter, this.documentRegistry, /*syntaxOnly*/ false);
1309                return new LanguageServiceShimObject(this, host, languageService);
1310            }
1311            catch (err) {
1312                logInternalError(host, err);
1313                throw err;
1314            }
1315        }
1316
1317        public createClassifierShim(logger: Logger): ClassifierShim {
1318            try {
1319                return new ClassifierShimObject(this, logger);
1320            }
1321            catch (err) {
1322                logInternalError(logger, err);
1323                throw err;
1324            }
1325        }
1326
1327        public createCoreServicesShim(host: CoreServicesShimHost): CoreServicesShim {
1328            try {
1329                const adapter = new CoreServicesShimHostAdapter(host);
1330                return new CoreServicesShimObject(this, host as Logger, adapter);
1331            }
1332            catch (err) {
1333                logInternalError(host as Logger, err);
1334                throw err;
1335            }
1336        }
1337
1338        public close(): void {
1339            // Forget all the registered shims
1340            clear(this._shims);
1341            this.documentRegistry = undefined;
1342        }
1343
1344        public registerShim(shim: Shim): void {
1345            this._shims.push(shim);
1346        }
1347
1348        public unregisterShim(shim: Shim): void {
1349            for (let i = 0; i < this._shims.length; i++) {
1350                if (this._shims[i] === shim) {
1351                    delete this._shims[i];
1352                    return;
1353                }
1354            }
1355
1356            throw new Error("Invalid operation");
1357        }
1358    }
1359}
1360
1361/* eslint-enable local/no-in-operator */
1362