namespace Harness . LanguageService { export function makeDefaultProxy ( info ) { const proxy = Object . create ( null ) ; const langSvc = info . languageService ; for ( const k of Object . keys ( langSvc ) ) { proxy [ k ] = function ( ) { return langSvc [ k ] . apply ( langSvc , arguments ) ; } ; } return proxy ; } export class ScriptInfo { public version = 1 ; public editRanges : { length : number ; textChangeRange : ts . TextChangeRange ; } [ ] = [ ] ; private lineMap : number [ ] | undefined ; constructor ( public fileName , public content , public isRootFile ) { this . setContent ( content ) ; } private setContent ( content ) { this . content = content ; this . lineMap = undefined ; } public getLineMap ( ) { return this . lineMap || ( this . lineMap = ts . computeLineStarts ( this . content ) ) ; } public updateContent ( content ) { this . editRanges = [ ] ; this . setContent ( content ) ; this . version ++ ; } public editContent ( start , end , newText ) { const prefix = this . content . substring ( 0 , start ) ; const middle = newText ; const suffix = this . content . substring ( end ) ; this . setContent ( prefix + middle + suffix ) ; this . editRanges . push ( { length : this . content . length , textChangeRange : ts . createTextChangeRange ( ts . createTextSpanFromBounds ( start , end ) , newText . length ) } ) ; this . version ++ ; } public getTextChangeRangeBetweenVersions ( startVersion , endVersion ) { if ( startVersion === endVersion ) { 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 ) { this . textSnapshot = scriptInfo . content ; this . version = scriptInfo . version ; } public getText ( start , end ) { return this . textSnapshot . substring ( start , end ) ; } public getLength ( ) { return this . textSnapshot . length ; } public getChangeRange ( oldScript ) { const oldShim = oldScript as ScriptSnapshot ; return this . scriptInfo . getTextChangeRangeBetweenVersions ( oldShim . version , this . version ) ; } } class ScriptSnapshotProxy implements ts . ScriptSnapshotShim { constructor ( private readonly scriptSnapshot ) { } public getText ( start , end ) { return this . scriptSnapshot . getText ( start , end ) ; } public getLength ( ) { return this . scriptSnapshot . getLength ( ) ; } public getChangeRange ( oldScript ) { const range = this . scriptSnapshot . getChangeRange ( ( oldScript as ScriptSnapshotProxy ) . scriptSnapshot ) ; return range && JSON . stringify ( range ) ; } } class DefaultHostCancellationToken implements ts . HostCancellationToken { public static readonly instance = new DefaultHostCancellationToken ( ) ; public isCancellationRequested ( ) { return false ; } } export interface LanguageServiceAdapter { getHost ( ) : LanguageServiceAdapterHost ; getLanguageService ( ) : ts . LanguageService ; getClassifier ( ) : ts . Classifier ; getPreProcessedFileInfo ( fileName , fileContents ) : ts . PreProcessedFileInfo ; } export abstract class LanguageServiceAdapterHost { public readonly sys = new fakes . System ( new vfs . FileSystem ( true , { cwd : virtualFileSystemRoot } ) ) ; public typesRegistry : ts . ESMap < string , void > | undefined ; private scriptInfos : collections . SortedMap < string , ScriptInfo > ; constructor ( protected cancellationToken = DefaultHostCancellationToken . instance , protected settings = ts . getDefaultCompilerOptions ( ) ) { this . scriptInfos = new collections . SortedMap ( { comparer : this . vfs . stringComparer , sort : "insertion" } ) ; } public get vfs ( ) { return this . sys . vfs ; } public getNewLine ( ) { return harnessNewLine ; } public getFilenames ( ) { const fileNames = [ ] ; this . scriptInfos . forEach ( scriptInfo => { if ( scriptInfo . isRootFile ) { fileNames . push ( scriptInfo . fileName ) ; } } ) ; return fileNames ; } public realpath ( path ) { try { return this . vfs . realpathSync ( path ) ; } catch { return path ; } } public directoryExists ( path ) { return this . vfs . statSync ( path ) . isDirectory ( ) ; } public getScriptInfo ( fileName ) { return this . scriptInfos . get ( vpath . resolve ( this . vfs . cwd ( ) , fileName ) ) ; } public addScript ( fileName , content , isRootFile ) { this . vfs . mkdirpSync ( vpath . dirname ( fileName ) ) ; this . vfs . writeFileSync ( fileName , content ) ; this . scriptInfos . set ( vpath . resolve ( this . vfs . cwd ( ) , fileName ) , new ScriptInfo ( fileName , content , isRootFile ) ) ; } public renameFileOrDirectory ( oldPath , newPath ) { this . vfs . mkdirpSync ( ts . getDirectoryPath ( newPath ) ) ; this . vfs . renameSync ( oldPath , newPath ) ; const updater = ts . getPathUpdater ( oldPath , newPath , ts . createGetCanonicalFileName ( this . useCaseSensitiveFileNames ( ) ) , undefined ) ; this . scriptInfos . forEach ( ( scriptInfo , key ) => { const newFileName = updater ( key ) ; if ( newFileName !== undefined ) { this . scriptInfos . delete ( key ) ; this . scriptInfos . set ( newFileName , scriptInfo ) ; scriptInfo . fileName = newFileName ; } } ) ; } public editScript ( fileName , start , end , newText ) { const script = this . getScriptInfo ( fileName ) ; if ( script ) { script . editContent ( start , end , newText ) ; this . vfs . mkdirpSync ( vpath . dirname ( fileName ) ) ; this . vfs . writeFileSync ( fileName , script . content ) ; return ; } throw new Error ( "No script with name '" + fileName + "'" ) ; } public openFile ( _fileName , _content ? , _scriptKindName ? ) { } param line param col public positionToLineAndCharacter ( fileName , position ) { const script = this . getScriptInfo ( fileName ) ! ; assert . isOk ( script ) ; return ts . computeLineAndCharacterOfPosition ( script . getLineMap ( ) , position ) ; } public lineAndCharacterToPosition ( fileName , lineAndCharacter ) { const script = this . getScriptInfo ( fileName ) ! ; assert . isOk ( script ) ; return ts . computePositionOfLineAndCharacter ( script . getLineMap ( ) , lineAndCharacter . line , lineAndCharacter . character ) ; } useCaseSensitiveFileNames ( ) { return ! this . vfs . ignoreCase ; } } class NativeLanguageServiceHost extends LanguageServiceAdapterHost implements ts . LanguageServiceHost , LanguageServiceAdapterHost { isKnownTypesPackageName ( name ) { return ! ! this . typesRegistry && this . typesRegistry . has ( name ) ; } getGlobalTypingsCacheLocation ( ) { return "/Library/Caches/typescript" ; } installPackage = ts . notImplemented ; getCompilationSettings ( ) { return this . settings ; } getCancellationToken ( ) { return this . cancellationToken ; } getDirectories ( path ) { return this . sys . getDirectories ( path ) ; } getCurrentDirectory ( ) { return virtualFileSystemRoot ; } getDefaultLibFileName ( ) { return Compiler . defaultLibFileName ; } getScriptFileNames ( ) { return this . getFilenames ( ) . filter ( ts . isAnySupportedFileExtension ) ; } getScriptSnapshot ( fileName ) { const script = this . getScriptInfo ( fileName ) ; return script ? new ScriptSnapshot ( script ) : undefined ; } getScriptKind ( ) { return ts . ScriptKind . Unknown ; } getScriptVersion ( fileName ) { const script = this . getScriptInfo ( fileName ) ; return script ? script . version . toString ( ) : undefined ! ; } directoryExists ( dirName ) { return this . sys . directoryExists ( dirName ) ; } fileExists ( fileName ) { return this . sys . fileExists ( fileName ) ; } readDirectory ( path , extensions ? , exclude ? , include ? , depth ? ) { return this . sys . readDirectory ( path , extensions , exclude , include , depth ) ; } readFile ( path ) { return this . sys . readFile ( path ) ; } realpath ( path ) { return this . sys . realpath ( path ) ; } getTypeRootsVersion ( ) { return 0 ; } log = ts . noop ; trace = ts . noop ; error = ts . noop ; } export class NativeLanguageServiceAdapter implements LanguageServiceAdapter { private host : NativeLanguageServiceHost ; constructor ( cancellationToken ? , options ? ) { this . host = new NativeLanguageServiceHost ( cancellationToken , options ) ; } getHost ( ) { return this . host ; } getLanguageService ( ) { return ts . createLanguageService ( this . host ) ; } getClassifier ( ) { return ts . createClassifier ( ) ; } getPreProcessedFileInfo ( fileName , fileContents ) { return ts . preProcessFile ( fileContents , true , ts . hasJSFileExtension ( fileName ) ) ; } } class ShimLanguageServiceHost extends LanguageServiceAdapterHost implements ts . LanguageServiceShimHost , ts . CoreServicesShimHost { private nativeHost : NativeLanguageServiceHost ; public getModuleResolutionsForFile : ( ( fileName : string ) => string ) | undefined ; public getTypeReferenceDirectiveResolutionsForFile : ( ( fileName : string ) => string ) | undefined ; constructor ( preprocessToResolve , cancellationToken ? , options ? ) { super ( cancellationToken , options ) ; this . nativeHost = new NativeLanguageServiceHost ( cancellationToken , options ) ; if ( preprocessToResolve ) { const compilerOptions = this . nativeHost . getCompilationSettings ( ) ; const moduleResolutionHost = { fileExists : fileName => this . getScriptInfo ( fileName ) !== undefined , readFile : fileName => { const scriptInfo = this . getScriptInfo ( fileName ) ; return scriptInfo && scriptInfo . content ; } , useCaseSensitiveFileNames : this . useCaseSensitiveFileNames ( ) } ; this . getModuleResolutionsForFile = ( fileName ) => { const scriptInfo = this . getScriptInfo ( fileName ) ! ; const preprocessInfo = ts . preProcessFile ( scriptInfo . content , true ) ; const imports = { } ; 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 , false ) ; const resolutions = { } ; 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 ( ) { return this . nativeHost . getFilenames ( ) ; } getScriptInfo ( fileName ) { return this . nativeHost . getScriptInfo ( fileName ) ; } addScript ( fileName , content , isRootFile ) { this . nativeHost . addScript ( fileName , content , isRootFile ) ; } editScript ( fileName , start , end , newText ) { this . nativeHost . editScript ( fileName , start , end , newText ) ; } positionToLineAndCharacter ( fileName , position ) { return this . nativeHost . positionToLineAndCharacter ( fileName , position ) ; } getCompilationSettings ( ) { return JSON . stringify ( this . nativeHost . getCompilationSettings ( ) ) ; } getCancellationToken ( ) { return this . nativeHost . getCancellationToken ( ) ; } getCurrentDirectory ( ) { return this . nativeHost . getCurrentDirectory ( ) ; } getDirectories ( path ) { return JSON . stringify ( this . nativeHost . getDirectories ( path ) ) ; } getDefaultLibFileName ( ) { return this . nativeHost . getDefaultLibFileName ( ) ; } getScriptFileNames ( ) { return JSON . stringify ( this . nativeHost . getScriptFileNames ( ) ) ; } getScriptSnapshot ( fileName ) { const nativeScriptSnapshot = this . nativeHost . getScriptSnapshot ( fileName ) ! ; return nativeScriptSnapshot && new ScriptSnapshotProxy ( nativeScriptSnapshot ) ; } getScriptKind ( ) { return this . nativeHost . getScriptKind ( ) ; } getScriptVersion ( fileName ) { return this . nativeHost . getScriptVersion ( fileName ) ; } getLocalizedDiagnosticMessages ( ) { return JSON . stringify ( { } ) ; } readDirectory = ts . notImplemented ; readDirectoryNames = ts . notImplemented ; readFileNames = ts . notImplemented ; fileExists ( fileName ) { return this . getScriptInfo ( fileName ) !== undefined ; } readFile ( fileName ) { const snapshot = this . nativeHost . getScriptSnapshot ( fileName ) ; return snapshot && ts . getSnapshotText ( snapshot ) ; } log ( s ) { this . nativeHost . log ( s ) ; } trace ( s ) { this . nativeHost . trace ( s ) ; } error ( s ) { this . nativeHost . error ( s ) ; } directoryExists ( ) { return true ; } } class ClassifierShimProxy implements ts . Classifier { constructor ( private shim ) { } getEncodedLexicalClassifications ( _text , _lexState , _classifyKeywordsInGenerics ? ) { return ts . notImplemented ( ) ; } getClassificationsForLine ( text , lexState , classifyKeywordsInGenerics ? ) { const result = this . shim . getClassificationsForLine ( text , lexState , classifyKeywordsInGenerics ) . split ( "\n" ) ; const entries = [ ] ; 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 ) { 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 ) { } cleanupSemanticCache ( ) { this . shim . cleanupSemanticCache ( ) ; } getSyntacticDiagnostics ( fileName ) { return unwrapJSONCallResult ( this . shim . getSyntacticDiagnostics ( fileName ) ) ; } getSemanticDiagnostics ( fileName ) { return unwrapJSONCallResult ( this . shim . getSemanticDiagnostics ( fileName ) ) ; } getSuggestionDiagnostics ( fileName ) { return unwrapJSONCallResult ( this . shim . getSuggestionDiagnostics ( fileName ) ) ; } getCompilerOptionsDiagnostics ( ) { return unwrapJSONCallResult ( this . shim . getCompilerOptionsDiagnostics ( ) ) ; } getSyntacticClassifications ( fileName , span ) { return unwrapJSONCallResult ( this . shim . getSyntacticClassifications ( fileName , span . start , span . length ) ) ; } getSemanticClassifications ( fileName , span , format ? ) { return unwrapJSONCallResult ( this . shim . getSemanticClassifications ( fileName , span . start , span . length , format ) ) ; } getEncodedSyntacticClassifications ( fileName , span ) { return unwrapJSONCallResult ( this . shim . getEncodedSyntacticClassifications ( fileName , span . start , span . length ) ) ; } getEncodedSemanticClassifications ( fileName , span , format ? ) { const responseFormat = format || ts . SemanticClassificationFormat . Original ; return unwrapJSONCallResult ( this . shim . getEncodedSemanticClassifications ( fileName , span . start , span . length , responseFormat ) ) ; } getCompletionsAtPosition ( fileName , position , preferences , formattingSettings ) { return unwrapJSONCallResult ( this . shim . getCompletionsAtPosition ( fileName , position , preferences , formattingSettings ) ) ; } getCompletionEntryDetails ( fileName , position , entryName , formatOptions , source , preferences , data ) { return unwrapJSONCallResult ( this . shim . getCompletionEntryDetails ( fileName , position , entryName , JSON . stringify ( formatOptions ) , source , preferences , data ) ) ; } getCompletionEntrySymbol ( ) { throw new Error ( "getCompletionEntrySymbol not implemented across the shim layer." ) ; } getQuickInfoAtPosition ( fileName , position ) { return unwrapJSONCallResult ( this . shim . getQuickInfoAtPosition ( fileName , position ) ) ; } getNameOrDottedNameSpan ( fileName , startPos , endPos ) { return unwrapJSONCallResult ( this . shim . getNameOrDottedNameSpan ( fileName , startPos , endPos ) ) ; } getBreakpointStatementAtPosition ( fileName , position ) { return unwrapJSONCallResult ( this . shim . getBreakpointStatementAtPosition ( fileName , position ) ) ; } getSignatureHelpItems ( fileName , position , options ) { return unwrapJSONCallResult ( this . shim . getSignatureHelpItems ( fileName , position , options ) ) ; } getRenameInfo ( fileName , position , options ? ) { return unwrapJSONCallResult ( this . shim . getRenameInfo ( fileName , position , options ) ) ; } getSmartSelectionRange ( fileName , position ) { return unwrapJSONCallResult ( this . shim . getSmartSelectionRange ( fileName , position ) ) ; } findRenameLocations ( fileName , position , findInStrings , findInComments , providePrefixAndSuffixTextForRename ? ) { return unwrapJSONCallResult ( this . shim . findRenameLocations ( fileName , position , findInStrings , findInComments , providePrefixAndSuffixTextForRename ) ) ; } getDefinitionAtPosition ( fileName , position ) { return unwrapJSONCallResult ( this . shim . getDefinitionAtPosition ( fileName , position ) ) ; } getDefinitionAndBoundSpan ( fileName , position ) { return unwrapJSONCallResult ( this . shim . getDefinitionAndBoundSpan ( fileName , position ) ) ; } getTypeDefinitionAtPosition ( fileName , position ) { return unwrapJSONCallResult ( this . shim . getTypeDefinitionAtPosition ( fileName , position ) ) ; } getImplementationAtPosition ( fileName , position ) { return unwrapJSONCallResult ( this . shim . getImplementationAtPosition ( fileName , position ) ) ; } getReferencesAtPosition ( fileName , position ) { return unwrapJSONCallResult ( this . shim . getReferencesAtPosition ( fileName , position ) ) ; } findReferences ( fileName , position ) { return unwrapJSONCallResult ( this . shim . findReferences ( fileName , position ) ) ; } getFileReferences ( fileName ) { return unwrapJSONCallResult ( this . shim . getFileReferences ( fileName ) ) ; } getOccurrencesAtPosition ( fileName , position ) { return unwrapJSONCallResult ( this . shim . getOccurrencesAtPosition ( fileName , position ) ) ; } getDocumentHighlights ( fileName , position , filesToSearch ) { return unwrapJSONCallResult ( this . shim . getDocumentHighlights ( fileName , position , JSON . stringify ( filesToSearch ) ) ) ; } getNavigateToItems ( searchValue ) { return unwrapJSONCallResult ( this . shim . getNavigateToItems ( searchValue ) ) ; } getNavigationBarItems ( fileName ) { return unwrapJSONCallResult ( this . shim . getNavigationBarItems ( fileName ) ) ; } getNavigationTree ( fileName ) { return unwrapJSONCallResult ( this . shim . getNavigationTree ( fileName ) ) ; } getOutliningSpans ( fileName ) { return unwrapJSONCallResult ( this . shim . getOutliningSpans ( fileName ) ) ; } getTodoComments ( fileName , descriptors ) { return unwrapJSONCallResult ( this . shim . getTodoComments ( fileName , JSON . stringify ( descriptors ) ) ) ; } getBraceMatchingAtPosition ( fileName , position ) { return unwrapJSONCallResult ( this . shim . getBraceMatchingAtPosition ( fileName , position ) ) ; } getIndentationAtPosition ( fileName , position , options ) { return unwrapJSONCallResult ( this . shim . getIndentationAtPosition ( fileName , position , JSON . stringify ( options ) ) ) ; } getFormattingEditsForRange ( fileName , start , end , options ) { return unwrapJSONCallResult ( this . shim . getFormattingEditsForRange ( fileName , start , end , JSON . stringify ( options ) ) ) ; } getFormattingEditsForDocument ( fileName , options ) { return unwrapJSONCallResult ( this . shim . getFormattingEditsForDocument ( fileName , JSON . stringify ( options ) ) ) ; } getFormattingEditsAfterKeystroke ( fileName , position , key , options ) { return unwrapJSONCallResult ( this . shim . getFormattingEditsAfterKeystroke ( fileName , position , key , JSON . stringify ( options ) ) ) ; } getDocCommentTemplateAtPosition ( fileName , position , options ? ) { return unwrapJSONCallResult ( this . shim . getDocCommentTemplateAtPosition ( fileName , position , options ) ) ; } isValidBraceCompletionAtPosition ( fileName , position , openingBrace ) { return unwrapJSONCallResult ( this . shim . isValidBraceCompletionAtPosition ( fileName , position , openingBrace ) ) ; } getJsxClosingTagAtPosition ( ) { throw new Error ( "Not supported on the shim." ) ; } getSpanOfEnclosingComment ( fileName , position , onlyMultiLine ) { return unwrapJSONCallResult ( this . shim . getSpanOfEnclosingComment ( fileName , position , onlyMultiLine ) ) ; } getCodeFixesAtPosition ( ) { throw new Error ( "Not supported on the shim." ) ; } getCombinedCodeFix = ts . notImplemented ; applyCodeActionCommand = ts . notImplemented ; getCodeFixDiagnostics ( ) { throw new Error ( "Not supported on the shim." ) ; } getEditsForRefactor ( ) { throw new Error ( "Not supported on the shim." ) ; } getApplicableRefactors ( ) { throw new Error ( "Not supported on the shim." ) ; } organizeImports ( _args , _formatOptions ) { throw new Error ( "Not supported on the shim." ) ; } getEditsForFileRename ( ) { throw new Error ( "Not supported on the shim." ) ; } prepareCallHierarchy ( fileName , position ) { return unwrapJSONCallResult ( this . shim . prepareCallHierarchy ( fileName , position ) ) ; } provideCallHierarchyIncomingCalls ( fileName , position ) { return unwrapJSONCallResult ( this . shim . provideCallHierarchyIncomingCalls ( fileName , position ) ) ; } provideCallHierarchyOutgoingCalls ( fileName , position ) { return unwrapJSONCallResult ( this . shim . provideCallHierarchyOutgoingCalls ( fileName , position ) ) ; } provideInlayHints ( fileName , span , preference ) { return unwrapJSONCallResult ( this . shim . provideInlayHints ( fileName , span , preference ) ) ; } getEmitOutput ( fileName ) { return unwrapJSONCallResult ( this . shim . getEmitOutput ( fileName ) ) ; } getProgram ( ) { throw new Error ( "Program can not be marshaled across the shim layer." ) ; } getAutoImportProvider ( ) { throw new Error ( "Program can not be marshaled across the shim layer." ) ; } getNonBoundSourceFile ( ) { throw new Error ( "SourceFile can not be marshaled across the shim layer." ) ; } getSourceFile ( ) { throw new Error ( "SourceFile can not be marshaled across the shim layer." ) ; } getSourceMapper ( ) { return ts . notImplemented ( ) ; } clearSourceMapperCache ( ) { return ts . notImplemented ( ) ; } toggleLineComment ( fileName , textRange ) { return unwrapJSONCallResult ( this . shim . toggleLineComment ( fileName , textRange ) ) ; } toggleMultilineComment ( fileName , textRange ) { return unwrapJSONCallResult ( this . shim . toggleMultilineComment ( fileName , textRange ) ) ; } commentSelection ( fileName , textRange ) { return unwrapJSONCallResult ( this . shim . commentSelection ( fileName , textRange ) ) ; } uncommentSelection ( fileName , textRange ) { return unwrapJSONCallResult ( this . shim . uncommentSelection ( fileName , textRange ) ) ; } dispose ( ) { this . shim . dispose ( { } ) ; } } export class ShimLanguageServiceAdapter implements LanguageServiceAdapter { private host : ShimLanguageServiceHost ; private factory : ts . TypeScriptServicesFactory ; constructor ( preprocessToResolve , cancellationToken ? , options ? ) { this . host = new ShimLanguageServiceHost ( preprocessToResolve , cancellationToken , options ) ; this . factory = new ts . TypeScriptServicesFactory ( ) ; } getHost ( ) { return this . host ; } getLanguageService ( ) { return new LanguageServiceShimProxy ( this . factory . createLanguageServiceShim ( this . host ) ) ; } getClassifier ( ) { return new ClassifierShimProxy ( this . factory . createClassifierShim ( this . host ) ) ; } getPreProcessedFileInfo ( fileName , fileContents ) { const coreServicesShim = this . factory . createCoreServicesShim ( this . host ) ; const shimResult = unwrapJSONCallResult ( coreServicesShim . getPreProcessedFileInfo ( fileName , ts . ScriptSnapshot . fromString ( fileContents ) ) ) ; const convertResult = { referencedFiles : [ ] , importedFiles : [ ] , ambientExternalModules : [ ] , isLibFile : shimResult . isLibFile , typeReferenceDirectives : [ ] , libReferenceDirectives : [ ] } ; 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 ; } } class SessionClientHost extends NativeLanguageServiceHost implements ts . server . SessionClientHost { private client ! : ts . server . SessionClient ; constructor ( cancellationToken , settings ) { super ( cancellationToken , settings ) ; } onMessage = ts . noop ; writeMessage = ts . noop ; setClient ( client ) { this . client = client ; } openFile ( fileName , content ? , scriptKindName ? ) { super . openFile ( fileName , content , scriptKindName ) ; this . client . openFile ( fileName , content , scriptKindName ) ; } editScript ( fileName , start , end , newText ) { const changeArgs = this . client . createChangeFileRequestArgs ( fileName , start , end , newText ) ; super . editScript ( fileName , start , end , newText ) ; this . client . changeFile ( fileName , changeArgs ) ; } } class SessionServerHost implements ts . server . ServerHost , ts . server . Logger { args : string [ ] = [ ] ; newLine : string ; useCaseSensitiveFileNames = false ; constructor ( private host ) { this . newLine = this . host . getNewLine ( ) ; } onMessage = ts . noop ; writeMessage = ts . noop ; write ( message ) { this . writeMessage ( message ) ; } readFile ( fileName ) { if ( ts . stringContains ( fileName , Compiler . defaultLibFileName ) ) { fileName = Compiler . defaultLibFileName ; } const snapshot = this . host . getScriptSnapshot ( fileName ) || this . host . getScriptSnapshot ( this . realpath ( fileName ) ) ; return snapshot && ts . getSnapshotText ( snapshot ) ; } realpath ( path ) { return this . host . realpath ( path ) ; } writeFile = ts . noop ; resolvePath ( path ) { return path ; } fileExists ( path ) { return this . host . fileExists ( path ) ; } directoryExists ( ) { return true ; } getExecutingFilePath ( ) { return "" ; } exit = ts . noop ; createDirectory ( _directoryName ) { return ts . notImplemented ( ) ; } getCurrentDirectory ( ) { return this . host . getCurrentDirectory ( ) ; } getDirectories ( path ) { return this . host . getDirectories ( path ) ; } getEnvironmentVariable ( name ) { return ts . sys . getEnvironmentVariable ( name ) ; } readDirectory ( path , extensions ? , exclude ? , include ? , depth ? ) { return this . host . readDirectory ( path , extensions , exclude , include , depth ) ; } watchFile ( ) { return { close : ts . noop } ; } watchDirectory ( ) { return { close : ts . noop } ; } close = ts . noop ; info ( message ) { this . host . log ( message ) ; } msg ( message ) { this . host . log ( message ) ; } loggingEnabled ( ) { return true ; } getLogFileName ( ) { return undefined ; } hasLevel ( ) { return false ; } startGroup ( ) { throw ts . notImplemented ( ) ; } endGroup ( ) { throw ts . notImplemented ( ) ; } perftrc ( message ) { return this . host . log ( message ) ; } setTimeout ( callback , ms , ... args ) { return setTimeout ( callback , ms , ... args ) ; } clearTimeout ( timeoutId ) { clearTimeout ( timeoutId ) ; } setImmediate ( callback , _ms , ... args ) { return setImmediate ( callback , args ) ; } clearImmediate ( timeoutId ) { clearImmediate ( timeoutId ) ; } createHash ( s ) { return mockHash ( s ) ; } require ( _initialDir , _moduleName ) { switch ( _moduleName ) { case "quickinfo-augmeneter" : return { module : ( ) => ( { create ( info ) { const proxy = makeDefaultProxy ( info ) ; const langSvc = info . languageService ; proxy . getQuickInfoAtPosition = function ( ) { const parts = langSvc . getQuickInfoAtPosition . apply ( langSvc , arguments ) ; if ( parts . displayParts . length > 0 ) { parts . displayParts [ 0 ] . text = "Proxied" ; } parts . displayParts . push ( { text : info . config . message , kind : "punctuation" } ) ; return parts ; } ; return proxy ; } } ) , error : undefined } ; case "create-thrower" : return { module : ( ) => ( { create ( ) { throw new Error ( "I am not a well-behaved plugin" ) ; } } ) , error : undefined } ; case "diagnostic-adder" : return { module : ( ) => ( { create ( info ) { const proxy = makeDefaultProxy ( info ) ; proxy . getSemanticDiagnostics = filename => { const prev = info . languageService . getSemanticDiagnostics ( filename ) ; const sourceFile = info . project . getSourceFile ( ts . toPath ( filename , undefined , ts . createGetCanonicalFileName ( info . serverHost . useCaseSensitiveFileNames ) ) ) ! ; prev . push ( { category : ts . DiagnosticCategory . Warning , file : sourceFile , code : 9999 , length : 3 , messageText : `Plugin diagnostic` , start : 0 } ) ; return prev ; } ; return proxy ; } } ) , error : undefined } ; case "configurable-diagnostic-adder" : let customMessage = "default message" ; return { module : ( ) => ( { create ( info ) { customMessage = info . config . message ; const proxy = makeDefaultProxy ( info ) ; proxy . getSemanticDiagnostics = filename => { const prev = info . languageService . getSemanticDiagnostics ( filename ) ; const sourceFile = info . project . getSourceFile ( ts . toPath ( filename , undefined , ts . createGetCanonicalFileName ( info . serverHost . useCaseSensitiveFileNames ) ) ) ! ; prev . push ( { category : ts . DiagnosticCategory . Error , file : sourceFile , code : 9999 , length : 3 , messageText : customMessage , start : 0 } ) ; return prev ; } ; return proxy ; } , onConfigurationChanged ( config ) { customMessage = config . message ; } } ) , error : undefined } ; default : return { module : undefined , error : new Error ( "Could not resolve module" ) } ; } } } class FourslashSession extends ts . server . Session { getText ( fileName ) { return ts . getSnapshotText ( this . projectService . getDefaultProjectForFile ( ts . server . toNormalizedPath ( fileName ) , true ) ! . getScriptSnapshot ( fileName ) ! ) ; } } export class ServerLanguageServiceAdapter implements LanguageServiceAdapter { private host : SessionClientHost ; private client : ts . server . SessionClient ; private server : FourslashSession ; constructor ( cancellationToken ? , options ? ) { const clientHost = new SessionClientHost ( cancellationToken , options ) ; const client = new ts . server . SessionClient ( clientHost ) ; const serverHost = new SessionServerHost ( clientHost ) ; const opts = { host : serverHost , cancellationToken : ts . server . nullCancellationToken , useSingleInferredProject : false , useInferredProjectPerProjectRoot : false , typingsInstaller : undefined ! , byteLength : Utils . byteLength , hrtime : process . hrtime , logger : serverHost , canUseEvents : true } ; this . server = new FourslashSession ( opts ) ; serverHost . writeMessage = client . onMessage . bind ( client ) ; clientHost . writeMessage = this . server . onMessage . bind ( this . server ) ; clientHost . setClient ( client ) ; this . client = client ; this . host = clientHost ; } getHost ( ) { return this . host ; } getLanguageService ( ) { return this . client ; } getClassifier ( ) { throw new Error ( "getClassifier is not available using the server interface." ) ; } getPreProcessedFileInfo ( ) { throw new Error ( "getPreProcessedFileInfo is not available using the server interface." ) ; } assertTextConsistent ( fileName ) { const serverText = this . server . getText ( fileName ) ; const clientText = this . host . readFile ( fileName ) ; ts . Debug . assert ( serverText === clientText , [ "Server and client text are inconsistent." , "" , "\x1b[1mServer\x1b[0m\x1b[31m:" , serverText , "" , "\x1b[1mClient\x1b[0m\x1b[31m:" , clientText , "" , "This probably means something is wrong with the fourslash infrastructure, not with the test." ] . join ( ts . sys . newLine ) ) ; } } }