/// /// /// /// namespace Harness.LanguageService { export class ScriptInfo { public version = 1; public editRanges: { length: number; textChangeRange: ts.TextChangeRange; }[] = []; private lineMap: number[] = undefined; constructor(public fileName: string, public content: string, public isRootFile: boolean) { this.setContent(content); } private setContent(content: string): void { this.content = content; this.lineMap = undefined; } public getLineMap(): number[] { return this.lineMap || (this.lineMap = ts.computeLineStarts(this.content)); } public updateContent(content: string): void { this.editRanges = []; this.setContent(content); this.version++; } public editContent(start: number, end: number, newText: string): void { // Apply edits const prefix = this.content.substring(0, start); const middle = newText; const suffix = this.content.substring(end); this.setContent(prefix + middle + suffix); // Store edit range + new length of script this.editRanges.push({ length: this.content.length, textChangeRange: ts.createTextChangeRange( ts.createTextSpanFromBounds(start, end), newText.length) }); // Update version # this.version++; } public getTextChangeRangeBetweenVersions(startVersion: number, endVersion: number): ts.TextChangeRange { if (startVersion === endVersion) { // No edits! return ts.unchangedTextChangeRange; } const initialEditRangeIndex = this.editRanges.length - (this.version - startVersion); const lastEditRangeIndex = this.editRanges.length - (this.version - endVersion); const entries = this.editRanges.slice(initialEditRangeIndex, lastEditRangeIndex); return ts.collapseTextChangeRangesAcrossMultipleVersions(entries.map(e => e.textChangeRange)); } } class ScriptSnapshot implements ts.IScriptSnapshot { public textSnapshot: string; public version: number; constructor(public scriptInfo: ScriptInfo) { this.textSnapshot = scriptInfo.content; this.version = scriptInfo.version; } public getText(start: number, end: number): string { return this.textSnapshot.substring(start, end); } public getLength(): number { return this.textSnapshot.length; } public getChangeRange(oldScript: ts.IScriptSnapshot): ts.TextChangeRange { const oldShim = oldScript; return this.scriptInfo.getTextChangeRangeBetweenVersions(oldShim.version, this.version); } } class ScriptSnapshotProxy implements ts.ScriptSnapshotShim { constructor(public scriptSnapshot: ts.IScriptSnapshot) { } public getText(start: number, end: number): string { return this.scriptSnapshot.getText(start, end); } public getLength(): number { return this.scriptSnapshot.getLength(); } public getChangeRange(oldScript: ts.ScriptSnapshotShim): string { const oldShim = oldScript; const range = this.scriptSnapshot.getChangeRange(oldShim.scriptSnapshot); if (range === undefined) { return undefined; } return JSON.stringify({ span: { start: range.span.start, length: range.span.length }, newLength: range.newLength }); } } class DefaultHostCancellationToken implements ts.HostCancellationToken { public static Instance = new DefaultHostCancellationToken(); public isCancellationRequested() { return false; } } export interface LanguageServiceAdapter { getHost(): LanguageServiceAdapterHost; getLanguageService(): ts.LanguageService; getClassifier(): ts.Classifier; getPreProcessedFileInfo(fileName: string, fileContents: string): ts.PreProcessedFileInfo; } export class LanguageServiceAdapterHost { protected virtualFileSystem: Utils.VirtualFileSystem = new Utils.VirtualFileSystem(virtualFileSystemRoot, /*useCaseSensitiveFilenames*/false); constructor(protected cancellationToken = DefaultHostCancellationToken.Instance, protected settings = ts.getDefaultCompilerOptions()) { } public getNewLine(): string { return "\r\n"; } public getFilenames(): string[] { const fileNames: string[] = []; for (const virtualEntry of this.virtualFileSystem.getAllFileEntries()){ const scriptInfo = virtualEntry.content; if (scriptInfo.isRootFile) { // only include root files here // usually it means that we won't include lib.d.ts in the list of root files so it won't mess the computation of compilation root dir. fileNames.push(scriptInfo.fileName); } } return fileNames; } public getScriptInfo(fileName: string): ScriptInfo { const fileEntry = this.virtualFileSystem.traversePath(fileName); return fileEntry && fileEntry.isFile() ? (fileEntry).content : undefined; } public addScript(fileName: string, content: string, isRootFile: boolean): void { this.virtualFileSystem.addFile(fileName, new ScriptInfo(fileName, content, isRootFile)); } public editScript(fileName: string, start: number, end: number, newText: string) { const script = this.getScriptInfo(fileName); if (script !== undefined) { script.editContent(start, end, newText); return; } throw new Error("No script with name '" + fileName + "'"); } public openFile(_fileName: string, _content?: string, _scriptKindName?: string): void { } /** * @param line 0 based index * @param col 0 based index */ public positionToLineAndCharacter(fileName: string, position: number): ts.LineAndCharacter { const script: ScriptInfo = this.getScriptInfo(fileName); assert.isOk(script); return ts.computeLineAndCharacterOfPosition(script.getLineMap(), position); } } /// Native adapter class NativeLanguageServiceHost extends LanguageServiceAdapterHost implements ts.LanguageServiceHost { getCompilationSettings() { return this.settings; } getCancellationToken() { return this.cancellationToken; } getDirectories(path: string): string[] { const dir = this.virtualFileSystem.traversePath(path); if (dir && dir.isDirectory()) { return ts.map((dir).getDirectories(), (d) => ts.combinePaths(path, d.name)); } return []; } getCurrentDirectory(): string { return virtualFileSystemRoot; } getDefaultLibFileName(): string { return Harness.Compiler.defaultLibFileName; } getScriptFileNames(): string[] { return this.getFilenames(); } getScriptSnapshot(fileName: string): ts.IScriptSnapshot { const script = this.getScriptInfo(fileName); return script ? new ScriptSnapshot(script) : undefined; } getScriptKind(): ts.ScriptKind { return ts.ScriptKind.Unknown; } getScriptVersion(fileName: string): string { const script = this.getScriptInfo(fileName); return script ? script.version.toString() : undefined; } fileExists(fileName: string): boolean { const script = this.getScriptSnapshot(fileName); return script !== undefined; } readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[]): string[] { return ts.matchFiles(path, extensions, exclude, include, /*useCaseSensitiveFileNames*/false, this.getCurrentDirectory(), (p) => this.virtualFileSystem.getAccessibleFileSystemEntries(p)); } readFile(path: string): string { const snapshot = this.getScriptSnapshot(path); return snapshot.getText(0, snapshot.getLength()); } getTypeRootsVersion() { return 0; } log(_: string): void { } trace(_: string): void { } error(_: string): void { } } export class NativeLanguageServiceAdapter implements LanguageServiceAdapter { private host: NativeLanguageServiceHost; constructor(cancellationToken?: ts.HostCancellationToken, options?: ts.CompilerOptions) { this.host = new NativeLanguageServiceHost(cancellationToken, options); } getHost() { return this.host; } getLanguageService(): ts.LanguageService { return ts.createLanguageService(this.host); } getClassifier(): ts.Classifier { return ts.createClassifier(); } getPreProcessedFileInfo(fileName: string, fileContents: string): ts.PreProcessedFileInfo { return ts.preProcessFile(fileContents, /* readImportFiles */ true, ts.hasJavaScriptFileExtension(fileName)); } } /// Shim adapter class ShimLanguageServiceHost extends LanguageServiceAdapterHost implements ts.LanguageServiceShimHost, ts.CoreServicesShimHost { private nativeHost: NativeLanguageServiceHost; public getModuleResolutionsForFile: (fileName: string) => string; public getTypeReferenceDirectiveResolutionsForFile: (fileName: string) => string; constructor(preprocessToResolve: boolean, cancellationToken?: ts.HostCancellationToken, options?: ts.CompilerOptions) { super(cancellationToken, options); this.nativeHost = new NativeLanguageServiceHost(cancellationToken, options); if (preprocessToResolve) { const compilerOptions = this.nativeHost.getCompilationSettings(); const moduleResolutionHost: ts.ModuleResolutionHost = { fileExists: fileName => this.getScriptInfo(fileName) !== undefined, readFile: fileName => { const scriptInfo = this.getScriptInfo(fileName); return scriptInfo && scriptInfo.content; } }; this.getModuleResolutionsForFile = (fileName) => { const scriptInfo = this.getScriptInfo(fileName); const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ true); const imports: ts.MapLike = {}; for (const module of preprocessInfo.importedFiles) { const resolutionInfo = ts.resolveModuleName(module.fileName, fileName, compilerOptions, moduleResolutionHost); if (resolutionInfo.resolvedModule) { imports[module.fileName] = resolutionInfo.resolvedModule.resolvedFileName; } } return JSON.stringify(imports); }; this.getTypeReferenceDirectiveResolutionsForFile = (fileName) => { const scriptInfo = this.getScriptInfo(fileName); if (scriptInfo) { const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ false); const resolutions: ts.MapLike = {}; const settings = this.nativeHost.getCompilationSettings(); for (const typeReferenceDirective of preprocessInfo.typeReferenceDirectives) { const resolutionInfo = ts.resolveTypeReferenceDirective(typeReferenceDirective.fileName, fileName, settings, moduleResolutionHost); if (resolutionInfo.resolvedTypeReferenceDirective.resolvedFileName) { resolutions[typeReferenceDirective.fileName] = resolutionInfo.resolvedTypeReferenceDirective; } } return JSON.stringify(resolutions); } else { return "[]"; } }; } } getFilenames(): string[] { return this.nativeHost.getFilenames(); } getScriptInfo(fileName: string): ScriptInfo { return this.nativeHost.getScriptInfo(fileName); } addScript(fileName: string, content: string, isRootFile: boolean): void { this.nativeHost.addScript(fileName, content, isRootFile); } editScript(fileName: string, start: number, end: number, newText: string): void { this.nativeHost.editScript(fileName, start, end, newText); } positionToLineAndCharacter(fileName: string, position: number): ts.LineAndCharacter { return this.nativeHost.positionToLineAndCharacter(fileName, position); } getCompilationSettings(): string { return JSON.stringify(this.nativeHost.getCompilationSettings()); } getCancellationToken(): ts.HostCancellationToken { return this.nativeHost.getCancellationToken(); } getCurrentDirectory(): string { return this.nativeHost.getCurrentDirectory(); } getDirectories(path: string): string { return JSON.stringify(this.nativeHost.getDirectories(path)); } getDefaultLibFileName(): string { return this.nativeHost.getDefaultLibFileName(); } getScriptFileNames(): string { return JSON.stringify(this.nativeHost.getScriptFileNames()); } getScriptSnapshot(fileName: string): ts.ScriptSnapshotShim { const nativeScriptSnapshot = this.nativeHost.getScriptSnapshot(fileName); return nativeScriptSnapshot && new ScriptSnapshotProxy(nativeScriptSnapshot); } getScriptKind(): ts.ScriptKind { return this.nativeHost.getScriptKind(); } getScriptVersion(fileName: string): string { return this.nativeHost.getScriptVersion(fileName); } getLocalizedDiagnosticMessages(): string { return JSON.stringify({}); } readDirectory(_rootDir: string, _extension: string): string { return ts.notImplemented(); } readDirectoryNames = ts.notImplemented; readFileNames = ts.notImplemented; fileExists(fileName: string) { return this.getScriptInfo(fileName) !== undefined; } readFile(fileName: string) { const snapshot = this.nativeHost.getScriptSnapshot(fileName); return snapshot && snapshot.getText(0, snapshot.getLength()); } log(s: string): void { this.nativeHost.log(s); } trace(s: string): void { this.nativeHost.trace(s); } error(s: string): void { this.nativeHost.error(s); } directoryExists(): boolean { // for tests pessimistically assume that directory always exists return true; } } class ClassifierShimProxy implements ts.Classifier { constructor(private shim: ts.ClassifierShim) { } getEncodedLexicalClassifications(_text: string, _lexState: ts.EndOfLineState, _classifyKeywordsInGenerics?: boolean): ts.Classifications { return ts.notImplemented(); } getClassificationsForLine(text: string, lexState: ts.EndOfLineState, classifyKeywordsInGenerics?: boolean): ts.ClassificationResult { const result = this.shim.getClassificationsForLine(text, lexState, classifyKeywordsInGenerics).split("\n"); const entries: ts.ClassificationInfo[] = []; let i = 0; let position = 0; for (; i < result.length - 1; i += 2) { const t = entries[i / 2] = { length: parseInt(result[i]), classification: parseInt(result[i + 1]) }; assert.isTrue(t.length > 0, "Result length should be greater than 0, got :" + t.length); position += t.length; } const finalLexState = parseInt(result[result.length - 1]); assert.equal(position, text.length, "Expected cumulative length of all entries to match the length of the source. expected: " + text.length + ", but got: " + position); return { finalLexState, entries }; } } function unwrapJSONCallResult(result: string): any { const parsedResult = JSON.parse(result); if (parsedResult.error) { throw new Error("Language Service Shim Error: " + JSON.stringify(parsedResult.error)); } else if (parsedResult.canceled) { throw new ts.OperationCanceledException(); } return parsedResult.result; } class LanguageServiceShimProxy implements ts.LanguageService { constructor(private shim: ts.LanguageServiceShim) { } cleanupSemanticCache(): void { this.shim.cleanupSemanticCache(); } getSyntacticDiagnostics(fileName: string): ts.Diagnostic[] { return unwrapJSONCallResult(this.shim.getSyntacticDiagnostics(fileName)); } getSemanticDiagnostics(fileName: string): ts.Diagnostic[] { return unwrapJSONCallResult(this.shim.getSemanticDiagnostics(fileName)); } getCompilerOptionsDiagnostics(): ts.Diagnostic[] { return unwrapJSONCallResult(this.shim.getCompilerOptionsDiagnostics()); } getSyntacticClassifications(fileName: string, span: ts.TextSpan): ts.ClassifiedSpan[] { return unwrapJSONCallResult(this.shim.getSyntacticClassifications(fileName, span.start, span.length)); } getSemanticClassifications(fileName: string, span: ts.TextSpan): ts.ClassifiedSpan[] { return unwrapJSONCallResult(this.shim.getSemanticClassifications(fileName, span.start, span.length)); } getEncodedSyntacticClassifications(fileName: string, span: ts.TextSpan): ts.Classifications { return unwrapJSONCallResult(this.shim.getEncodedSyntacticClassifications(fileName, span.start, span.length)); } getEncodedSemanticClassifications(fileName: string, span: ts.TextSpan): ts.Classifications { return unwrapJSONCallResult(this.shim.getEncodedSemanticClassifications(fileName, span.start, span.length)); } getCompletionsAtPosition(fileName: string, position: number): ts.CompletionInfo { return unwrapJSONCallResult(this.shim.getCompletionsAtPosition(fileName, position)); } getCompletionEntryDetails(fileName: string, position: number, entryName: string): ts.CompletionEntryDetails { return unwrapJSONCallResult(this.shim.getCompletionEntryDetails(fileName, position, entryName)); } getCompletionEntrySymbol(): ts.Symbol { throw new Error("getCompletionEntrySymbol not implemented across the shim layer."); } getQuickInfoAtPosition(fileName: string, position: number): ts.QuickInfo { return unwrapJSONCallResult(this.shim.getQuickInfoAtPosition(fileName, position)); } getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): ts.TextSpan { return unwrapJSONCallResult(this.shim.getNameOrDottedNameSpan(fileName, startPos, endPos)); } getBreakpointStatementAtPosition(fileName: string, position: number): ts.TextSpan { return unwrapJSONCallResult(this.shim.getBreakpointStatementAtPosition(fileName, position)); } getSignatureHelpItems(fileName: string, position: number): ts.SignatureHelpItems { return unwrapJSONCallResult(this.shim.getSignatureHelpItems(fileName, position)); } getRenameInfo(fileName: string, position: number): ts.RenameInfo { return unwrapJSONCallResult(this.shim.getRenameInfo(fileName, position)); } findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): ts.RenameLocation[] { return unwrapJSONCallResult(this.shim.findRenameLocations(fileName, position, findInStrings, findInComments)); } getDefinitionAtPosition(fileName: string, position: number): ts.DefinitionInfo[] { return unwrapJSONCallResult(this.shim.getDefinitionAtPosition(fileName, position)); } getTypeDefinitionAtPosition(fileName: string, position: number): ts.DefinitionInfo[] { return unwrapJSONCallResult(this.shim.getTypeDefinitionAtPosition(fileName, position)); } getImplementationAtPosition(fileName: string, position: number): ts.ImplementationLocation[] { return unwrapJSONCallResult(this.shim.getImplementationAtPosition(fileName, position)); } getReferencesAtPosition(fileName: string, position: number): ts.ReferenceEntry[] { return unwrapJSONCallResult(this.shim.getReferencesAtPosition(fileName, position)); } findReferences(fileName: string, position: number): ts.ReferencedSymbol[] { return unwrapJSONCallResult(this.shim.findReferences(fileName, position)); } getOccurrencesAtPosition(fileName: string, position: number): ts.ReferenceEntry[] { return unwrapJSONCallResult(this.shim.getOccurrencesAtPosition(fileName, position)); } getDocumentHighlights(fileName: string, position: number, filesToSearch: string[]): ts.DocumentHighlights[] { return unwrapJSONCallResult(this.shim.getDocumentHighlights(fileName, position, JSON.stringify(filesToSearch))); } getNavigateToItems(searchValue: string): ts.NavigateToItem[] { return unwrapJSONCallResult(this.shim.getNavigateToItems(searchValue)); } getNavigationBarItems(fileName: string): ts.NavigationBarItem[] { return unwrapJSONCallResult(this.shim.getNavigationBarItems(fileName)); } getNavigationTree(fileName: string): ts.NavigationTree { return unwrapJSONCallResult(this.shim.getNavigationTree(fileName)); } getOutliningSpans(fileName: string): ts.OutliningSpan[] { return unwrapJSONCallResult(this.shim.getOutliningSpans(fileName)); } getTodoComments(fileName: string, descriptors: ts.TodoCommentDescriptor[]): ts.TodoComment[] { return unwrapJSONCallResult(this.shim.getTodoComments(fileName, JSON.stringify(descriptors))); } getBraceMatchingAtPosition(fileName: string, position: number): ts.TextSpan[] { return unwrapJSONCallResult(this.shim.getBraceMatchingAtPosition(fileName, position)); } getIndentationAtPosition(fileName: string, position: number, options: ts.EditorOptions): number { return unwrapJSONCallResult(this.shim.getIndentationAtPosition(fileName, position, JSON.stringify(options))); } getFormattingEditsForRange(fileName: string, start: number, end: number, options: ts.FormatCodeOptions): ts.TextChange[] { return unwrapJSONCallResult(this.shim.getFormattingEditsForRange(fileName, start, end, JSON.stringify(options))); } getFormattingEditsForDocument(fileName: string, options: ts.FormatCodeOptions): ts.TextChange[] { return unwrapJSONCallResult(this.shim.getFormattingEditsForDocument(fileName, JSON.stringify(options))); } getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: ts.FormatCodeOptions): ts.TextChange[] { return unwrapJSONCallResult(this.shim.getFormattingEditsAfterKeystroke(fileName, position, key, JSON.stringify(options))); } getDocCommentTemplateAtPosition(fileName: string, position: number): ts.TextInsertion { return unwrapJSONCallResult(this.shim.getDocCommentTemplateAtPosition(fileName, position)); } isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): boolean { return unwrapJSONCallResult(this.shim.isValidBraceCompletionAtPosition(fileName, position, openingBrace)); } getCodeFixesAtPosition(): ts.CodeAction[] { throw new Error("Not supported on the shim."); } getEmitOutput(fileName: string): ts.EmitOutput { return unwrapJSONCallResult(this.shim.getEmitOutput(fileName)); } getProgram(): ts.Program { throw new Error("Program can not be marshaled across the shim layer."); } getNonBoundSourceFile(): ts.SourceFile { throw new Error("SourceFile can not be marshaled across the shim layer."); } getSourceFile(): ts.SourceFile { throw new Error("SourceFile can not be marshaled across the shim layer."); } dispose(): void { this.shim.dispose({}); } } export class ShimLanguageServiceAdapter implements LanguageServiceAdapter { private host: ShimLanguageServiceHost; private factory: ts.TypeScriptServicesFactory; constructor(preprocessToResolve: boolean, cancellationToken?: ts.HostCancellationToken, options?: ts.CompilerOptions) { this.host = new ShimLanguageServiceHost(preprocessToResolve, cancellationToken, options); this.factory = new TypeScript.Services.TypeScriptServicesFactory(); } getHost() { return this.host; } getLanguageService(): ts.LanguageService { return new LanguageServiceShimProxy(this.factory.createLanguageServiceShim(this.host)); } getClassifier(): ts.Classifier { return new ClassifierShimProxy(this.factory.createClassifierShim(this.host)); } getPreProcessedFileInfo(fileName: string, fileContents: string): ts.PreProcessedFileInfo { let shimResult: { referencedFiles: ts.IFileReference[]; typeReferenceDirectives: ts.IFileReference[]; importedFiles: ts.IFileReference[]; isLibFile: boolean; }; const coreServicesShim = this.factory.createCoreServicesShim(this.host); shimResult = unwrapJSONCallResult(coreServicesShim.getPreProcessedFileInfo(fileName, ts.ScriptSnapshot.fromString(fileContents))); const convertResult: ts.PreProcessedFileInfo = { referencedFiles: [], importedFiles: [], ambientExternalModules: [], isLibFile: shimResult.isLibFile, typeReferenceDirectives: [] }; ts.forEach(shimResult.referencedFiles, refFile => { convertResult.referencedFiles.push({ fileName: refFile.path, pos: refFile.position, end: refFile.position + refFile.length }); }); ts.forEach(shimResult.importedFiles, importedFile => { convertResult.importedFiles.push({ fileName: importedFile.path, pos: importedFile.position, end: importedFile.position + importedFile.length }); }); ts.forEach(shimResult.typeReferenceDirectives, typeRefDirective => { convertResult.importedFiles.push({ fileName: typeRefDirective.path, pos: typeRefDirective.position, end: typeRefDirective.position + typeRefDirective.length }); }); return convertResult; } } // Server adapter class SessionClientHost extends NativeLanguageServiceHost implements ts.server.SessionClientHost { private client: ts.server.SessionClient; constructor(cancellationToken: ts.HostCancellationToken, settings: ts.CompilerOptions) { super(cancellationToken, settings); } onMessage(): void { } writeMessage(): void { } setClient(client: ts.server.SessionClient) { this.client = client; } openFile(fileName: string, content?: string, scriptKindName?: "TS" | "JS" | "TSX" | "JSX"): void { super.openFile(fileName, content, scriptKindName); this.client.openFile(fileName, content, scriptKindName); } editScript(fileName: string, start: number, end: number, newText: string) { super.editScript(fileName, start, end, newText); this.client.changeFile(fileName, start, end, newText); } } class SessionServerHost implements ts.server.ServerHost, ts.server.Logger { args: string[] = []; newLine: string; useCaseSensitiveFileNames = false; constructor(private host: NativeLanguageServiceHost) { this.newLine = this.host.getNewLine(); } onMessage(): void { } writeMessage(_message: string): void { } write(message: string): void { this.writeMessage(message); } readFile(fileName: string): string { if (fileName.indexOf(Harness.Compiler.defaultLibFileName) >= 0) { fileName = Harness.Compiler.defaultLibFileName; } const snapshot = this.host.getScriptSnapshot(fileName); return snapshot && snapshot.getText(0, snapshot.getLength()); } writeFile(): void { } resolvePath(path: string): string { return path; } fileExists(path: string): boolean { return !!this.host.getScriptSnapshot(path); } directoryExists(): boolean { // for tests assume that directory exists return true; } getExecutingFilePath(): string { return ""; } exit(): void { } createDirectory(_directoryName: string): void { return ts.notImplemented(); } getCurrentDirectory(): string { return this.host.getCurrentDirectory(); } getDirectories(): string[] { return []; } getEnvironmentVariable(name: string): string { return ts.sys.getEnvironmentVariable(name); } readDirectory(_path: string, _extension?: string[], _exclude?: string[], _include?: string[]): string[] { return ts.notImplemented(); } watchFile(): ts.FileWatcher { return { close: ts.noop }; } watchDirectory(): ts.FileWatcher { return { close: ts.noop }; } close(): void { } info(message: string): void { return this.host.log(message); } msg(message: string) { return this.host.log(message); } loggingEnabled() { return true; } getLogFileName(): string { return undefined; } hasLevel() { return false; } endGroup(): void { } perftrc(message: string): void { return this.host.log(message); } startGroup(): void { } setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): any { return setTimeout(callback, ms, args); } clearTimeout(timeoutId: any): void { clearTimeout(timeoutId); } setImmediate(callback: (...args: any[]) => void, _ms: number, ...args: any[]): any { return setImmediate(callback, args); } clearImmediate(timeoutId: any): void { clearImmediate(timeoutId); } createHash(s: string) { return s; } } export class ServerLanguageServiceAdapter implements LanguageServiceAdapter { private host: SessionClientHost; private client: ts.server.SessionClient; constructor(cancellationToken?: ts.HostCancellationToken, options?: ts.CompilerOptions) { // This is the main host that tests use to direct tests const clientHost = new SessionClientHost(cancellationToken, options); const client = new ts.server.SessionClient(clientHost); // This host is just a proxy for the clientHost, it uses the client // host to answer server queries about files on disk const serverHost = new SessionServerHost(clientHost); const server = new ts.server.Session(serverHost, { isCancellationRequested: () => false }, /*useOneInferredProject*/ false, /*typingsInstaller*/ undefined, Utils.byteLength, process.hrtime, serverHost, /*canUseEvents*/ true); // Fake the connection between the client and the server serverHost.writeMessage = client.onMessage.bind(client); clientHost.writeMessage = server.onMessage.bind(server); // Wire the client to the host to get notifications when a file is open // or edited. clientHost.setClient(client); // Set the properties this.client = client; this.host = clientHost; } getHost() { return this.host; } getLanguageService(): ts.LanguageService { return this.client; } getClassifier(): ts.Classifier { throw new Error("getClassifier is not available using the server interface."); } getPreProcessedFileInfo(): ts.PreProcessedFileInfo { throw new Error("getPreProcessedFileInfo is not available using the server interface."); } } }