@@ -927,14 +927,6 @@ namespace ts {
927927
928928 /// Language Service
929929
930- // Information about a specific host file.
931- interface HostFileInformation {
932- hostFileName : string ;
933- version : string ;
934- scriptSnapshot : IScriptSnapshot ;
935- scriptKind : ScriptKind ;
936- }
937-
938930 /* @internal */
939931 export interface DisplayPartsSymbolWriter extends EmitTextWriter {
940932 displayParts ( ) : SymbolDisplayPart [ ] ;
@@ -988,82 +980,6 @@ namespace ts {
988980 return codefix . getSupportedErrorCodes ( ) ;
989981 }
990982
991- // Either it will be file name if host doesnt have file or it will be the host's file information
992- type CachedHostFileInformation = HostFileInformation | string ;
993-
994- // Cache host information about script Should be refreshed
995- // at each language service public entry point, since we don't know when
996- // the set of scripts handled by the host changes.
997- class HostCache {
998- private fileNameToEntry : ESMap < Path , CachedHostFileInformation > ;
999- private currentDirectory : string ;
1000-
1001- constructor ( private host : LanguageServiceHost , getCanonicalFileName : GetCanonicalFileName ) {
1002- // script id => script index
1003- this . currentDirectory = host . getCurrentDirectory ( ) ;
1004- this . fileNameToEntry = new Map ( ) ;
1005-
1006- // Initialize the list with the root file names
1007- const rootFileNames = host . getScriptFileNames ( ) ;
1008- tracing ?. push ( tracing . Phase . Session , "initializeHostCache" , { count : rootFileNames . length } ) ;
1009- for ( const fileName of rootFileNames ) {
1010- this . createEntry ( fileName , toPath ( fileName , this . currentDirectory , getCanonicalFileName ) ) ;
1011- }
1012- tracing ?. pop ( ) ;
1013- }
1014-
1015- private createEntry ( fileName : string , path : Path ) {
1016- let entry : CachedHostFileInformation ;
1017- const scriptSnapshot = this . host . getScriptSnapshot ( fileName ) ;
1018- if ( scriptSnapshot ) {
1019- entry = {
1020- hostFileName : fileName ,
1021- version : this . host . getScriptVersion ( fileName ) ,
1022- scriptSnapshot,
1023- scriptKind : getScriptKind ( fileName , this . host )
1024- } ;
1025- }
1026- else {
1027- entry = fileName ;
1028- }
1029-
1030- this . fileNameToEntry . set ( path , entry ) ;
1031- return entry ;
1032- }
1033-
1034- public getEntryByPath ( path : Path ) : CachedHostFileInformation | undefined {
1035- return this . fileNameToEntry . get ( path ) ;
1036- }
1037-
1038- public getHostFileInformation ( path : Path ) : HostFileInformation | undefined {
1039- const entry = this . fileNameToEntry . get ( path ) ;
1040- return ! isString ( entry ) ? entry : undefined ;
1041- }
1042-
1043- public getOrCreateEntryByPath ( fileName : string , path : Path ) : HostFileInformation {
1044- const info = this . getEntryByPath ( path ) || this . createEntry ( fileName , path ) ;
1045- return isString ( info ) ? undefined ! : info ; // TODO: GH#18217
1046- }
1047-
1048- public getRootFileNames ( ) : string [ ] {
1049- const names : string [ ] = [ ] ;
1050- this . fileNameToEntry . forEach ( entry => {
1051- if ( isString ( entry ) ) {
1052- names . push ( entry ) ;
1053- }
1054- else {
1055- names . push ( entry . hostFileName ) ;
1056- }
1057- } ) ;
1058- return names ;
1059- }
1060-
1061- public getScriptSnapshot ( path : Path ) : IScriptSnapshot {
1062- const file = this . getHostFileInformation ( path ) ;
1063- return ( file && file . scriptSnapshot ) ! ; // TODO: GH#18217
1064- }
1065- }
1066-
1067983 class SyntaxTreeCache {
1068984 // For our syntactic only features, we also keep a cache of the syntax tree for the
1069985 // currently edited file.
@@ -1367,37 +1283,17 @@ namespace ts {
13671283 lastTypesRootVersion = typeRootsVersion ;
13681284 }
13691285
1286+ const rootFileNames = host . getScriptFileNames ( ) ;
1287+
13701288 // Get a fresh cache of the host information
1371- let hostCache : HostCache | undefined = new HostCache ( host , getCanonicalFileName ) ;
1372- const rootFileNames = hostCache . getRootFileNames ( ) ;
13731289 const newSettings = host . getCompilationSettings ( ) || getDefaultCompilerOptions ( ) ;
13741290 const hasInvalidatedResolution : HasInvalidatedResolution = host . hasInvalidatedResolution || returnFalse ;
13751291 const hasChangedAutomaticTypeDirectiveNames = maybeBind ( host , host . hasChangedAutomaticTypeDirectiveNames ) ;
13761292 const projectReferences = host . getProjectReferences ?.( ) ;
13771293 let parsedCommandLines : ESMap < Path , ParsedCommandLine | false > | undefined ;
1378- const parseConfigHost : ParseConfigFileHost = {
1379- useCaseSensitiveFileNames,
1380- fileExists,
1381- readFile,
1382- readDirectory,
1383- trace : maybeBind ( host , host . trace ) ,
1384- getCurrentDirectory : ( ) => currentDirectory ,
1385- onUnRecoverableConfigFileDiagnostic : noop ,
1386- } ;
1387-
1388- // If the program is already up-to-date, we can reuse it
1389- if ( isProgramUptoDate ( program , rootFileNames , newSettings , ( _path , fileName ) => host . getScriptVersion ( fileName ) , fileExists , hasInvalidatedResolution , hasChangedAutomaticTypeDirectiveNames , getParsedCommandLine , projectReferences ) ) {
1390- return ;
1391- }
1392-
1393- // IMPORTANT - It is critical from this moment onward that we do not check
1394- // cancellation tokens. We are about to mutate source files from a previous program
1395- // instance. If we cancel midway through, we may end up in an inconsistent state where
1396- // the program points to old source files that have been invalidated because of
1397- // incremental parsing.
13981294
13991295 // Now create a new compiler
1400- const compilerHost : CompilerHost = {
1296+ let compilerHost : CompilerHost | undefined = {
14011297 getSourceFile : getOrCreateSourceFile ,
14021298 getSourceFileByPath : getOrCreateSourceFileByPath ,
14031299 getCancellationToken : ( ) => cancellationToken ,
@@ -1407,8 +1303,8 @@ namespace ts {
14071303 getDefaultLibFileName : options => host . getDefaultLibFileName ( options ) ,
14081304 writeFile : noop ,
14091305 getCurrentDirectory : ( ) => currentDirectory ,
1410- fileExists,
1411- readFile,
1306+ fileExists : fileName => host . fileExists ( fileName ) ,
1307+ readFile : fileName => host . readFile && host . readFile ( fileName ) ,
14121308 getSymlinkCache : maybeBind ( host , host . getSymlinkCache ) ,
14131309 realpath : maybeBind ( host , host . realpath ) ,
14141310 directoryExists : directoryName => {
@@ -1417,20 +1313,54 @@ namespace ts {
14171313 getDirectories : path => {
14181314 return host . getDirectories ? host . getDirectories ( path ) : [ ] ;
14191315 } ,
1420- readDirectory,
1316+ readDirectory : ( path : string , extensions ?: readonly string [ ] , exclude ?: readonly string [ ] , include ?: readonly string [ ] , depth ?: number ) => {
1317+ Debug . checkDefined ( host . readDirectory , "'LanguageServiceHost.readDirectory' must be implemented to correctly process 'projectReferences'" ) ;
1318+ return host . readDirectory ! ( path , extensions , exclude , include , depth ) ;
1319+ } ,
14211320 onReleaseOldSourceFile,
14221321 onReleaseParsedCommandLine,
14231322 hasInvalidatedResolution,
14241323 hasChangedAutomaticTypeDirectiveNames,
1425- trace : parseConfigHost . trace ,
1324+ trace : maybeBind ( host , host . trace ) ,
14261325 resolveModuleNames : maybeBind ( host , host . resolveModuleNames ) ,
14271326 getModuleResolutionCache : maybeBind ( host , host . getModuleResolutionCache ) ,
14281327 resolveTypeReferenceDirectives : maybeBind ( host , host . resolveTypeReferenceDirectives ) ,
14291328 useSourceOfProjectReferenceRedirect : maybeBind ( host , host . useSourceOfProjectReferenceRedirect ) ,
14301329 getParsedCommandLine,
14311330 } ;
1331+
1332+ const originalGetSourceFile = compilerHost . getSourceFile ;
1333+
1334+ const { getSourceFileWithCache } = changeCompilerHostLikeToUseCache (
1335+ compilerHost ,
1336+ fileName => toPath ( fileName , currentDirectory , getCanonicalFileName ) ,
1337+ ( ...args ) => originalGetSourceFile . call ( compilerHost , ...args )
1338+ ) ;
1339+ compilerHost . getSourceFile = getSourceFileWithCache ! ;
1340+
14321341 host . setCompilerHost ?.( compilerHost ) ;
14331342
1343+ const parseConfigHost : ParseConfigFileHost = {
1344+ useCaseSensitiveFileNames,
1345+ fileExists : fileName => compilerHost ! . fileExists ( fileName ) ,
1346+ readFile : fileName => compilerHost ! . readFile ( fileName ) ,
1347+ readDirectory : ( ...args ) => compilerHost ! . readDirectory ! ( ...args ) ,
1348+ trace : compilerHost . trace ,
1349+ getCurrentDirectory : compilerHost . getCurrentDirectory ,
1350+ onUnRecoverableConfigFileDiagnostic : noop ,
1351+ } ;
1352+
1353+ // If the program is already up-to-date, we can reuse it
1354+ if ( isProgramUptoDate ( program , rootFileNames , newSettings , ( _path , fileName ) => host . getScriptVersion ( fileName ) , fileName => compilerHost ! . fileExists ( fileName ) , hasInvalidatedResolution , hasChangedAutomaticTypeDirectiveNames , getParsedCommandLine , projectReferences ) ) {
1355+ return ;
1356+ }
1357+
1358+ // IMPORTANT - It is critical from this moment onward that we do not check
1359+ // cancellation tokens. We are about to mutate source files from a previous program
1360+ // instance. If we cancel midway through, we may end up in an inconsistent state where
1361+ // the program points to old source files that have been invalidated because of
1362+ // incremental parsing.
1363+
14341364 const documentRegistryBucketKey = documentRegistry . getKeyForCompilationSettings ( newSettings ) ;
14351365 const options : CreateProgramOptions = {
14361366 rootNames : rootFileNames ,
@@ -1441,9 +1371,9 @@ namespace ts {
14411371 } ;
14421372 program = createProgram ( options ) ;
14431373
1444- // hostCache is captured in the closure for 'getOrCreateSourceFile' but it should not be used past this point.
1445- // It needs to be cleared to allow all collected snapshots to be released
1446- hostCache = undefined ;
1374+ // 'getOrCreateSourceFile' depends on caching but should be used past this point.
1375+ // After this point, the cache needs to be cleared to allow all collected snapshots to be released
1376+ compilerHost = undefined ;
14471377 parsedCommandLines = undefined ;
14481378
14491379 // We reset this cache on structure invalidation so we don't hold on to outdated files for long; however we can't use the `compilerHost` above,
@@ -1492,29 +1422,6 @@ namespace ts {
14921422 }
14931423 }
14941424
1495- function fileExists ( fileName : string ) : boolean {
1496- const path = toPath ( fileName , currentDirectory , getCanonicalFileName ) ;
1497- const entry = hostCache && hostCache . getEntryByPath ( path ) ;
1498- return entry ?
1499- ! isString ( entry ) :
1500- ( ! ! host . fileExists && host . fileExists ( fileName ) ) ;
1501- }
1502-
1503- function readFile ( fileName : string ) {
1504- // stub missing host functionality
1505- const path = toPath ( fileName , currentDirectory , getCanonicalFileName ) ;
1506- const entry = hostCache && hostCache . getEntryByPath ( path ) ;
1507- if ( entry ) {
1508- return isString ( entry ) ? undefined : getSnapshotText ( entry . scriptSnapshot ) ;
1509- }
1510- return host . readFile && host . readFile ( fileName ) ;
1511- }
1512-
1513- function readDirectory ( path : string , extensions ?: readonly string [ ] , exclude ?: readonly string [ ] , include ?: readonly string [ ] , depth ?: number ) {
1514- Debug . checkDefined ( host . readDirectory , "'LanguageServiceHost.readDirectory' must be implemented to correctly process 'projectReferences'" ) ;
1515- return host . readDirectory ! ( path , extensions , exclude , include , depth ) ;
1516- }
1517-
15181425 // Release any files we have acquired in the old program but are
15191426 // not part of the new program.
15201427 function onReleaseOldSourceFile ( oldSourceFile : SourceFile , oldOptions : CompilerOptions ) {
@@ -1527,15 +1434,18 @@ namespace ts {
15271434 }
15281435
15291436 function getOrCreateSourceFileByPath ( fileName : string , path : Path , _languageVersion : ScriptTarget , _onError ?: ( message : string ) => void , shouldCreateNewSourceFile ?: boolean ) : SourceFile | undefined {
1530- Debug . assert ( hostCache !== undefined , "getOrCreateSourceFileByPath called after typical CompilerHost lifetime, check the callstack something with a reference to an old host." ) ;
1437+ Debug . assert ( compilerHost , "getOrCreateSourceFileByPath called after typical CompilerHost lifetime, check the callstack something with a reference to an old host." ) ;
15311438 // The program is asking for this file, check first if the host can locate it.
15321439 // If the host can not locate the file, then it does not exist. return undefined
15331440 // to the program to allow reporting of errors for missing files.
1534- const hostFileInformation = hostCache && hostCache . getOrCreateEntryByPath ( fileName , path ) ;
1535- if ( ! hostFileInformation ) {
1441+ const scriptSnapshot = host . getScriptSnapshot ( fileName ) ;
1442+ if ( ! scriptSnapshot ) {
15361443 return undefined ;
15371444 }
15381445
1446+ const scriptKind = getScriptKind ( fileName , host ) ;
1447+ const scriptVersion = host . getScriptVersion ( fileName ) ;
1448+
15391449 // Check if the language version has changed since we last created a program; if they are the same,
15401450 // it is safe to reuse the sourceFiles; if not, then the shape of the AST can change, and the oldSourceFile
15411451 // can not be reused. we have to dump all syntax trees and create new ones.
@@ -1568,8 +1478,8 @@ namespace ts {
15681478 // We do not support the scenario where a host can modify a registered
15691479 // file's script kind, i.e. in one project some file is treated as ".ts"
15701480 // and in another as ".js"
1571- if ( hostFileInformation . scriptKind === oldSourceFile . scriptKind ) {
1572- return documentRegistry . updateDocumentWithKey ( fileName , path , host , documentRegistryBucketKey , hostFileInformation . scriptSnapshot , hostFileInformation . version , hostFileInformation . scriptKind ) ;
1481+ if ( scriptKind === oldSourceFile . scriptKind ) {
1482+ return documentRegistry . updateDocumentWithKey ( fileName , path , host , documentRegistryBucketKey , scriptSnapshot , scriptVersion , scriptKind ) ;
15731483 }
15741484 else {
15751485 // Release old source file and fall through to aquire new file with new script kind
@@ -1581,7 +1491,7 @@ namespace ts {
15811491 }
15821492
15831493 // Could not find this file in the old program, create a new SourceFile for it.
1584- return documentRegistry . acquireDocumentWithKey ( fileName , path , host , documentRegistryBucketKey , hostFileInformation . scriptSnapshot , hostFileInformation . version , hostFileInformation . scriptKind ) ;
1494+ return documentRegistry . acquireDocumentWithKey ( fileName , path , host , documentRegistryBucketKey , scriptSnapshot , scriptVersion , scriptKind ) ;
15851495 }
15861496 }
15871497
0 commit comments