@@ -927,14 +927,6 @@ namespace ts {
927
927
928
928
/// Language Service
929
929
930
- // Information about a specific host file.
931
- interface HostFileInformation {
932
- hostFileName : string ;
933
- version : string ;
934
- scriptSnapshot : IScriptSnapshot ;
935
- scriptKind : ScriptKind ;
936
- }
937
-
938
930
/* @internal */
939
931
export interface DisplayPartsSymbolWriter extends EmitTextWriter {
940
932
displayParts ( ) : SymbolDisplayPart [ ] ;
@@ -988,82 +980,6 @@ namespace ts {
988
980
return codefix . getSupportedErrorCodes ( ) ;
989
981
}
990
982
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
-
1067
983
class SyntaxTreeCache {
1068
984
// For our syntactic only features, we also keep a cache of the syntax tree for the
1069
985
// currently edited file.
@@ -1367,37 +1283,17 @@ namespace ts {
1367
1283
lastTypesRootVersion = typeRootsVersion ;
1368
1284
}
1369
1285
1286
+ const rootFileNames = host . getScriptFileNames ( ) ;
1287
+
1370
1288
// Get a fresh cache of the host information
1371
- let hostCache : HostCache | undefined = new HostCache ( host , getCanonicalFileName ) ;
1372
- const rootFileNames = hostCache . getRootFileNames ( ) ;
1373
1289
const newSettings = host . getCompilationSettings ( ) || getDefaultCompilerOptions ( ) ;
1374
1290
const hasInvalidatedResolution : HasInvalidatedResolution = host . hasInvalidatedResolution || returnFalse ;
1375
1291
const hasChangedAutomaticTypeDirectiveNames = maybeBind ( host , host . hasChangedAutomaticTypeDirectiveNames ) ;
1376
1292
const projectReferences = host . getProjectReferences ?.( ) ;
1377
1293
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.
1398
1294
1399
1295
// Now create a new compiler
1400
- const compilerHost : CompilerHost = {
1296
+ let compilerHost : CompilerHost | undefined = {
1401
1297
getSourceFile : getOrCreateSourceFile ,
1402
1298
getSourceFileByPath : getOrCreateSourceFileByPath ,
1403
1299
getCancellationToken : ( ) => cancellationToken ,
@@ -1407,8 +1303,8 @@ namespace ts {
1407
1303
getDefaultLibFileName : options => host . getDefaultLibFileName ( options ) ,
1408
1304
writeFile : noop ,
1409
1305
getCurrentDirectory : ( ) => currentDirectory ,
1410
- fileExists,
1411
- readFile,
1306
+ fileExists : fileName => host . fileExists ( fileName ) ,
1307
+ readFile : fileName => host . readFile && host . readFile ( fileName ) ,
1412
1308
getSymlinkCache : maybeBind ( host , host . getSymlinkCache ) ,
1413
1309
realpath : maybeBind ( host , host . realpath ) ,
1414
1310
directoryExists : directoryName => {
@@ -1417,20 +1313,54 @@ namespace ts {
1417
1313
getDirectories : path => {
1418
1314
return host . getDirectories ? host . getDirectories ( path ) : [ ] ;
1419
1315
} ,
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
+ } ,
1421
1320
onReleaseOldSourceFile,
1422
1321
onReleaseParsedCommandLine,
1423
1322
hasInvalidatedResolution,
1424
1323
hasChangedAutomaticTypeDirectiveNames,
1425
- trace : parseConfigHost . trace ,
1324
+ trace : maybeBind ( host , host . trace ) ,
1426
1325
resolveModuleNames : maybeBind ( host , host . resolveModuleNames ) ,
1427
1326
getModuleResolutionCache : maybeBind ( host , host . getModuleResolutionCache ) ,
1428
1327
resolveTypeReferenceDirectives : maybeBind ( host , host . resolveTypeReferenceDirectives ) ,
1429
1328
useSourceOfProjectReferenceRedirect : maybeBind ( host , host . useSourceOfProjectReferenceRedirect ) ,
1430
1329
getParsedCommandLine,
1431
1330
} ;
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
+
1432
1341
host . setCompilerHost ?.( compilerHost ) ;
1433
1342
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
+
1434
1364
const documentRegistryBucketKey = documentRegistry . getKeyForCompilationSettings ( newSettings ) ;
1435
1365
const options : CreateProgramOptions = {
1436
1366
rootNames : rootFileNames ,
@@ -1441,9 +1371,9 @@ namespace ts {
1441
1371
} ;
1442
1372
program = createProgram ( options ) ;
1443
1373
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 ;
1447
1377
parsedCommandLines = undefined ;
1448
1378
1449
1379
// 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 {
1492
1422
}
1493
1423
}
1494
1424
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
-
1518
1425
// Release any files we have acquired in the old program but are
1519
1426
// not part of the new program.
1520
1427
function onReleaseOldSourceFile ( oldSourceFile : SourceFile , oldOptions : CompilerOptions ) {
@@ -1527,15 +1434,18 @@ namespace ts {
1527
1434
}
1528
1435
1529
1436
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." ) ;
1531
1438
// The program is asking for this file, check first if the host can locate it.
1532
1439
// If the host can not locate the file, then it does not exist. return undefined
1533
1440
// 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 ) {
1536
1443
return undefined ;
1537
1444
}
1538
1445
1446
+ const scriptKind = getScriptKind ( fileName , host ) ;
1447
+ const scriptVersion = host . getScriptVersion ( fileName ) ;
1448
+
1539
1449
// Check if the language version has changed since we last created a program; if they are the same,
1540
1450
// it is safe to reuse the sourceFiles; if not, then the shape of the AST can change, and the oldSourceFile
1541
1451
// can not be reused. we have to dump all syntax trees and create new ones.
@@ -1568,8 +1478,8 @@ namespace ts {
1568
1478
// We do not support the scenario where a host can modify a registered
1569
1479
// file's script kind, i.e. in one project some file is treated as ".ts"
1570
1480
// 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 ) ;
1573
1483
}
1574
1484
else {
1575
1485
// Release old source file and fall through to aquire new file with new script kind
@@ -1581,7 +1491,7 @@ namespace ts {
1581
1491
}
1582
1492
1583
1493
// 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 ) ;
1585
1495
}
1586
1496
}
1587
1497
0 commit comments