namespace ts { /**
     * The document registry represents a store of SourceFile objects that can be shared between
     * multiple LanguageService instances. A LanguageService instance holds on the SourceFile (AST)
     * of files in the context.
     * SourceFile objects account for most of the memory usage by the language service. Sharing
     * the same DocumentRegistry instance between different instances of LanguageService allow
     * for more efficient memory utilization since all projects will share at least the library
     * file (lib.d.ts).
     *
     * A more advanced use of the document registry is to serialize sourceFile objects to disk
     * and re-hydrate them when needed.
     *
     * To create a default DocumentRegistry, use createDocumentRegistry to create one, and pass it
     * to all subsequent createLanguageService calls.
     */ export interface DocumentRegistry { param fileName param compilationSettings param scriptSnapshot param version acquireDocument ( fileName , compilationSettings , scriptSnapshot , version , scriptKind ? ) : SourceFile ; acquireDocumentWithKey ( fileName , path , compilationSettings , key , scriptSnapshot , version , scriptKind ? ) : SourceFile ; param fileName param compilationSettings param scriptSnapshot param version updateDocument ( fileName , compilationSettings , scriptSnapshot , version , scriptKind ? ) : SourceFile ; updateDocumentWithKey ( fileName , path , compilationSettings , key , scriptSnapshot , version , scriptKind ? ) : SourceFile ; getKeyForCompilationSettings ( settings ) : DocumentRegistryBucketKey ; param fileName param compilationSettings deprecated releaseDocument ( fileName , compilationSettings ) : void ; param fileName param compilationSettings param scriptKind releaseDocument ( fileName , compilationSettings , scriptKind ) : void ; deprecated releaseDocumentWithKey ( path , key ) : void ; releaseDocumentWithKey ( path , key , scriptKind ) : void ; getLanguageServiceRefCounts ( path , scriptKind ) : [ string , number | undefined ] [ ] ; reportStats ( ) : string ; } export interface ExternalDocumentCache { setDocument ( key , path , sourceFile ) : void ; getDocument ( key , path ) : SourceFile | undefined ; } export type DocumentRegistryBucketKey = string & { __bucketKey : any ; } ; interface DocumentRegistryEntry { sourceFile : SourceFile ; languageServiceRefCount : number ; } type BucketEntry = DocumentRegistryEntry | ESMap < ScriptKind , DocumentRegistryEntry > ; function isDocumentRegistryEntry ( entry ) { return ! ! ( entry as DocumentRegistryEntry ) . sourceFile ; } export function createDocumentRegistry ( useCaseSensitiveFileNames ? , currentDirectory ? ) { return createDocumentRegistryInternal ( useCaseSensitiveFileNames , currentDirectory ) ; } export function createDocumentRegistryInternal ( useCaseSensitiveFileNames ? , currentDirectory = "" , externalCache ? ) { const buckets = new Map < DocumentRegistryBucketKey , ESMap < Path , BucketEntry > > ( ) ; const getCanonicalFileName = createGetCanonicalFileName ( ! ! useCaseSensitiveFileNames ) ; function reportStats ( ) { const bucketInfoArray = arrayFrom ( buckets . keys ( ) ) . filter ( name => name && name . charAt ( 0 ) === "_" ) . map ( name => { const entries = buckets . get ( name ) ! ; const sourceFiles = [ ] ; entries . forEach ( ( entry , name ) => { if ( isDocumentRegistryEntry ( entry ) ) { sourceFiles . push ( { name , scriptKind : entry . sourceFile . scriptKind , refCount : entry . languageServiceRefCount } ) ; } else { entry . forEach ( ( value , scriptKind ) => sourceFiles . push ( { name , scriptKind , refCount : value . languageServiceRefCount } ) ) ; } } ) ; sourceFiles . sort ( ( x , y ) => y . refCount - x . refCount ) ; return { bucket : name , sourceFiles } ; } ) ; return JSON . stringify ( bucketInfoArray , undefined , 2 ) ; } function acquireDocument ( fileName , compilationSettings , scriptSnapshot , version , scriptKind ? ) { const path = toPath ( fileName , currentDirectory , getCanonicalFileName ) ; const key = getKeyForCompilationSettings ( compilationSettings ) ; return acquireDocumentWithKey ( fileName , path , compilationSettings , key , scriptSnapshot , version , scriptKind ) ; } function acquireDocumentWithKey ( fileName , path , compilationSettings , key , scriptSnapshot , version , scriptKind ? ) { return acquireOrUpdateDocument ( fileName , path , compilationSettings , key , scriptSnapshot , version , true , scriptKind ) ; } function updateDocument ( fileName , compilationSettings , scriptSnapshot , version , scriptKind ? ) { const path = toPath ( fileName , currentDirectory , getCanonicalFileName ) ; const key = getKeyForCompilationSettings ( compilationSettings ) ; return updateDocumentWithKey ( fileName , path , compilationSettings , key , scriptSnapshot , version , scriptKind ) ; } function updateDocumentWithKey ( fileName , path , compilationSettings , key , scriptSnapshot , version , scriptKind ? ) { return acquireOrUpdateDocument ( fileName , path , compilationSettings , key , scriptSnapshot , version , false , scriptKind ) ; } function getDocumentRegistryEntry ( bucketEntry , scriptKind ) { const entry = isDocumentRegistryEntry ( bucketEntry ) ? bucketEntry : bucketEntry . get ( Debug . checkDefined ( scriptKind , "If there are more than one scriptKind's for same document the scriptKind should be provided" ) ) ; Debug . assert ( scriptKind === undefined || ! entry || entry . sourceFile . scriptKind === scriptKind , `Script kind should match provided ScriptKind:${ scriptKind } and sourceFile.scriptKind: ${ entry ?. sourceFile . scriptKind }, !entry: ${ ! entry }` ) ; return entry ; } function acquireOrUpdateDocument ( fileName , path , compilationSettings , key , scriptSnapshot , version , acquiring , scriptKind ? ) { scriptKind = ensureScriptKind ( fileName , scriptKind ) ; const scriptTarget = scriptKind === ScriptKind . JSON ? ScriptTarget . JSON : getEmitScriptTarget ( compilationSettings ) ; const bucket = getOrUpdate ( buckets , key , ( ) => new Map ( ) ) ; const bucketEntry = bucket . get ( path ) ; let entry = bucketEntry && getDocumentRegistryEntry ( bucketEntry , scriptKind ) ; if ( ! entry && externalCache ) { const sourceFile = externalCache . getDocument ( key , path ) ; if ( sourceFile ) { Debug . assert ( acquiring ) ; entry = { sourceFile , languageServiceRefCount : 0 } ; setBucketEntry ( ) ; } } if ( ! entry ) { const sourceFile = createLanguageServiceSourceFile ( fileName , scriptSnapshot , scriptTarget , version , false , scriptKind ) ; if ( externalCache ) { externalCache . setDocument ( key , path , sourceFile ) ; } entry = { sourceFile , languageServiceRefCount : 1 , } ; setBucketEntry ( ) ; } else { if ( entry . sourceFile . version !== version ) { entry . sourceFile = updateLanguageServiceSourceFile ( entry . sourceFile , scriptSnapshot , version , scriptSnapshot . getChangeRange ( entry . sourceFile . scriptSnapshot ! ) ) ; if ( externalCache ) { externalCache . setDocument ( key , path , entry . sourceFile ) ; } } if ( acquiring ) { entry . languageServiceRefCount ++ ; } } Debug . assert ( entry . languageServiceRefCount !== 0 ) ; return entry . sourceFile ; function setBucketEntry ( ) { if ( ! bucketEntry ) { bucket . set ( path , entry ! ) ; } else if ( isDocumentRegistryEntry ( bucketEntry ) ) { const scriptKindMap = new Map < ScriptKind , DocumentRegistryEntry > ( ) ; scriptKindMap . set ( bucketEntry . sourceFile . scriptKind , bucketEntry ) ; scriptKindMap . set ( scriptKind ! , entry ! ) ; bucket . set ( path , scriptKindMap ) ; } else { bucketEntry . set ( scriptKind ! , entry ! ) ; } } } function releaseDocument ( fileName , compilationSettings , scriptKind ? ) { const path = toPath ( fileName , currentDirectory , getCanonicalFileName ) ; const key = getKeyForCompilationSettings ( compilationSettings ) ; return releaseDocumentWithKey ( path , key , scriptKind ) ; } function releaseDocumentWithKey ( path , key , scriptKind ? ) { const bucket = Debug . checkDefined ( buckets . get ( key ) ) ; const bucketEntry = bucket . get ( path ) ! ; const entry = getDocumentRegistryEntry ( bucketEntry , scriptKind ) ! ; entry . languageServiceRefCount -- ; Debug . assert ( entry . languageServiceRefCount >= 0 ) ; if ( entry . languageServiceRefCount === 0 ) { if ( isDocumentRegistryEntry ( bucketEntry ) ) { bucket . delete ( path ) ; } else { bucketEntry . delete ( scriptKind ! ) ; if ( bucketEntry . size === 1 ) { bucket . set ( path , firstDefinedIterator ( bucketEntry . values ( ) , identity ) ! ) ; } } } } function getLanguageServiceRefCounts ( path , scriptKind ) { return arrayFrom ( buckets . entries ( ) , ( [ key , bucket ] ) : [ string , number | undefined ] => { const bucketEntry = bucket . get ( path ) ; const entry = bucketEntry && getDocumentRegistryEntry ( bucketEntry , scriptKind ) ; return [ key , entry && entry . languageServiceRefCount ] ; } ) ; } return { acquireDocument , acquireDocumentWithKey , updateDocument , updateDocumentWithKey , releaseDocument , releaseDocumentWithKey , getLanguageServiceRefCounts , reportStats , getKeyForCompilationSettings } ; } function getKeyForCompilationSettings ( settings ) { return sourceFileAffectingCompilerOptions . map ( option => getCompilerOptionValue ( settings , option ) ) . join ( "|" ) as DocumentRegistryBucketKey ; } }