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