@@ -862,6 +862,7 @@ module ts {
862
862
getLocalizedDiagnosticMessages ?( ) : any ;
863
863
getCancellationToken ?( ) : CancellationToken ;
864
864
getCurrentDirectory ( ) : string ;
865
+ getDefaultLibFilename ( options : CompilerOptions ) : string ;
865
866
log ? ( s : string ) : void ;
866
867
trace ? ( s : string ) : void ;
867
868
error ? ( s : string ) : void ;
@@ -1319,9 +1320,9 @@ module ts {
1319
1320
1320
1321
// Information about a specific host file.
1321
1322
interface HostFileInformation {
1322
- filename : string ;
1323
+ hostFilename : string ;
1323
1324
version : string ;
1324
- sourceText ? : IScriptSnapshot ;
1325
+ scriptSnapshot : IScriptSnapshot ;
1325
1326
}
1326
1327
1327
1328
interface DocumentRegistryEntry {
@@ -1409,60 +1410,69 @@ module ts {
1409
1410
// script id => script index
1410
1411
this . filenameToEntry = { } ;
1411
1412
1412
- var filenames = host . getScriptFileNames ( ) ;
1413
- for ( var i = 0 , n = filenames . length ; i < n ; i ++ ) {
1414
- var filename = filenames [ i ] ;
1415
- this . filenameToEntry [ normalizeSlashes ( filename ) ] = {
1416
- filename : filename ,
1417
- version : host . getScriptVersion ( filename )
1418
- } ;
1413
+ // Initialize the list with the root file names
1414
+ var rootFilenames = host . getScriptFileNames ( ) ;
1415
+ for ( var i = 0 , n = rootFilenames . length ; i < n ; i ++ ) {
1416
+ this . createEntry ( rootFilenames [ i ] ) ;
1419
1417
}
1420
1418
1419
+ // store the compilation settings
1421
1420
this . _compilationSettings = host . getCompilationSettings ( ) || getDefaultCompilerOptions ( ) ;
1422
1421
}
1423
1422
1424
1423
public compilationSettings ( ) {
1425
1424
return this . _compilationSettings ;
1426
1425
}
1427
1426
1427
+ private createEntry ( filename : string ) {
1428
+ var entry : HostFileInformation ;
1429
+ var scriptSnapshot = this . host . getScriptSnapshot ( filename ) ;
1430
+ if ( scriptSnapshot ) {
1431
+ entry = {
1432
+ hostFilename : filename ,
1433
+ version : this . host . getScriptVersion ( filename ) ,
1434
+ scriptSnapshot : scriptSnapshot
1435
+ } ;
1436
+ }
1437
+
1438
+ return this . filenameToEntry [ normalizeSlashes ( filename ) ] = entry ;
1439
+ }
1440
+
1428
1441
public getEntry ( filename : string ) : HostFileInformation {
1429
- filename = normalizeSlashes ( filename ) ;
1430
- return lookUp ( this . filenameToEntry , filename ) ;
1442
+ return lookUp ( this . filenameToEntry , normalizeSlashes ( filename ) ) ;
1431
1443
}
1432
1444
1433
1445
public contains ( filename : string ) : boolean {
1434
- return ! ! this . getEntry ( filename ) ;
1446
+ return hasProperty ( this . filenameToEntry , normalizeSlashes ( filename ) ) ;
1435
1447
}
1436
1448
1437
- public getHostfilename ( filename : string ) {
1438
- var hostCacheEntry = this . getEntry ( filename ) ;
1439
- if ( hostCacheEntry ) {
1440
- return hostCacheEntry . filename ;
1449
+ public getOrCreateEntry ( filename : string ) : HostFileInformation {
1450
+ if ( this . contains ( filename ) ) {
1451
+ return this . getEntry ( filename ) ;
1441
1452
}
1442
- return filename ;
1453
+
1454
+ return this . createEntry ( filename ) ;
1443
1455
}
1444
1456
1445
- public getFilenames ( ) : string [ ] {
1457
+ public getRootFilenames ( ) : string [ ] {
1446
1458
var fileNames : string [ ] = [ ] ;
1447
1459
1448
1460
forEachKey ( this . filenameToEntry , key => {
1449
- if ( hasProperty ( this . filenameToEntry , key ) )
1461
+ if ( hasProperty ( this . filenameToEntry , key ) && this . filenameToEntry [ key ] )
1450
1462
fileNames . push ( key ) ;
1451
1463
} ) ;
1452
1464
1453
1465
return fileNames ;
1454
1466
}
1455
1467
1456
1468
public getVersion ( filename : string ) : string {
1457
- return this . getEntry ( filename ) . version ;
1469
+ var file = this . getEntry ( filename ) ;
1470
+ return file && file . version ;
1458
1471
}
1459
1472
1460
1473
public getScriptSnapshot ( filename : string ) : IScriptSnapshot {
1461
1474
var file = this . getEntry ( filename ) ;
1462
- if ( ! file . sourceText ) {
1463
- file . sourceText = this . host . getScriptSnapshot ( file . filename ) ;
1464
- }
1465
- return file . sourceText ;
1475
+ return file && file . scriptSnapshot ;
1466
1476
}
1467
1477
1468
1478
public getChangeRange ( filename : string , lastKnownVersion : string , oldScriptSnapshot : IScriptSnapshot ) : TextChangeRange {
@@ -1918,14 +1928,12 @@ module ts {
1918
1928
export function createLanguageService ( host : LanguageServiceHost , documentRegistry : DocumentRegistry ) : LanguageService {
1919
1929
var syntaxTreeCache : SyntaxTreeCache = new SyntaxTreeCache ( host ) ;
1920
1930
var ruleProvider : formatting . RulesProvider ;
1921
- var hostCache : HostCache ; // A cache of all the information about the files on the host side.
1922
1931
var program : Program ;
1923
1932
1924
1933
// this checker is used to answer all LS questions except errors
1925
1934
var typeInfoResolver : TypeChecker ;
1926
1935
1927
1936
var useCaseSensitivefilenames = false ;
1928
- var sourceFilesByName : Map < SourceFile > = { } ;
1929
1937
var documentRegistry = documentRegistry ;
1930
1938
var cancellationToken = new CancellationTokenObject ( host . getCancellationToken && host . getCancellationToken ( ) ) ;
1931
1939
var activeCompletionSession : CompletionSession ; // The current active completion session, used to get the completion entry details
@@ -1946,7 +1954,7 @@ module ts {
1946
1954
}
1947
1955
1948
1956
function getSourceFile ( filename : string ) : SourceFile {
1949
- return lookUp ( sourceFilesByName , getCanonicalFileName ( filename ) ) ;
1957
+ return program . getSourceFile ( getCanonicalFileName ( filename ) ) ;
1950
1958
}
1951
1959
1952
1960
function getDiagnosticsProducingTypeChecker ( ) {
@@ -1963,112 +1971,108 @@ module ts {
1963
1971
return ruleProvider ;
1964
1972
}
1965
1973
1966
- function sourceFileUpToDate ( sourceFile : SourceFile ) : boolean {
1967
- return sourceFile && sourceFile . version === hostCache . getVersion ( sourceFile . filename ) ;
1968
- }
1969
-
1970
- function programUpToDate ( ) : boolean {
1971
- // If we haven't create a program yet, then it is not up-to-date
1972
- if ( ! program ) {
1973
- return false ;
1974
- }
1975
-
1976
- // If number of files in the program do not match, it is not up-to-date
1977
- var hostFilenames = hostCache . getFilenames ( ) ;
1978
- if ( program . getSourceFiles ( ) . length !== hostFilenames . length ) {
1979
- return false ;
1980
- }
1981
-
1982
- // If any file is not up-to-date, then the whole program is not up-to-date
1983
- for ( var i = 0 , n = hostFilenames . length ; i < n ; i ++ ) {
1984
- if ( ! sourceFileUpToDate ( program . getSourceFile ( hostFilenames [ i ] ) ) ) {
1985
- return false ;
1986
- }
1987
- }
1988
-
1989
- // If the compilation settings do no match, then the program is not up-to-date
1990
- return compareDataObjects ( program . getCompilerOptions ( ) , hostCache . compilationSettings ( ) ) ;
1991
- }
1992
-
1993
1974
function synchronizeHostData ( ) : void {
1994
- // Reset the cache at start of every refresh
1995
- hostCache = new HostCache ( host ) ;
1975
+ // Get a fresh cache of the host information
1976
+ var hostCache = new HostCache ( host ) ;
1996
1977
1997
1978
// If the program is already up-to-date, we can reuse it
1998
1979
if ( programUpToDate ( ) ) {
1999
1980
return ;
2000
1981
}
2001
1982
2002
- var compilationSettings = hostCache . compilationSettings ( ) ;
1983
+ var oldSettings = program && program . getCompilerOptions ( ) ;
1984
+ var newSettings = hostCache . compilationSettings ( ) ;
1985
+ var changesInCompilationSettingsAffectSyntax = oldSettings && oldSettings . target !== newSettings . target ;
2003
1986
2004
- // Now, remove any files from the compiler that are no longer in the host.
2005
- var oldProgram = program ;
2006
- if ( oldProgram ) {
2007
- var oldSettings = program . getCompilerOptions ( ) ;
2008
- // If the language version changed, then that affects what types of things we parse. So
2009
- // we have to dump all syntax trees.
2010
- // TODO: handle propagateEnumConstants
2011
- // TODO: is module still needed
2012
- var settingsChangeAffectsSyntax = oldSettings . target !== compilationSettings . target || oldSettings . module !== compilationSettings . module ;
1987
+ // Now create a new compiler
1988
+ var newProgram = createProgram ( hostCache . getRootFilenames ( ) , newSettings , {
1989
+ getSourceFile : getOrCreateSourceFile ,
1990
+ getCancellationToken : ( ) => cancellationToken ,
1991
+ getCanonicalFileName : ( filename ) => useCaseSensitivefilenames ? filename : filename . toLowerCase ( ) ,
1992
+ useCaseSensitiveFileNames : ( ) => useCaseSensitivefilenames ,
1993
+ getNewLine : ( ) => host . getNewLine ? host . getNewLine ( ) : "\r\n" ,
1994
+ getDefaultLibFilename : ( options ) => host . getDefaultLibFilename ( options ) ,
1995
+ writeFile : ( filename , data , writeByteOrderMark ) => { } ,
1996
+ getCurrentDirectory : ( ) => host . getCurrentDirectory ( )
1997
+ } ) ;
2013
1998
2014
- var changesInCompilationSettingsAffectSyntax =
2015
- oldSettings && compilationSettings && ! compareDataObjects ( oldSettings , compilationSettings ) && settingsChangeAffectsSyntax ;
1999
+ // Release any files we have acquired in the old program but are
2000
+ // not part of the new program.
2001
+ if ( program ) {
2016
2002
var oldSourceFiles = program . getSourceFiles ( ) ;
2017
-
2018
2003
for ( var i = 0 , n = oldSourceFiles . length ; i < n ; i ++ ) {
2019
- cancellationToken . throwIfCancellationRequested ( ) ;
2020
2004
var filename = oldSourceFiles [ i ] . filename ;
2021
- if ( ! hostCache . contains ( filename ) || changesInCompilationSettingsAffectSyntax ) {
2005
+ if ( ! newProgram . getSourceFile ( filename ) || changesInCompilationSettingsAffectSyntax ) {
2022
2006
documentRegistry . releaseDocument ( filename , oldSettings ) ;
2023
- delete sourceFilesByName [ getCanonicalFileName ( filename ) ] ;
2024
2007
}
2025
2008
}
2026
2009
}
2027
2010
2028
- // Now, for every file the host knows about, either add the file (if the compiler
2029
- // doesn't know about it.). Or notify the compiler about any changes (if it does
2030
- // know about it.)
2031
- var hostfilenames = hostCache . getFilenames ( ) ;
2032
- for ( var i = 0 , n = hostfilenames . length ; i < n ; i ++ ) {
2033
- var filename = hostfilenames [ i ] ;
2011
+ program = newProgram ;
2012
+ typeInfoResolver = program . getTypeChecker ( /*produceDiagnostics*/ false ) ;
2034
2013
2035
- var version = hostCache . getVersion ( filename ) ;
2036
- var scriptSnapshot = hostCache . getScriptSnapshot ( filename ) ;
2014
+ return ;
2037
2015
2038
- var sourceFile : SourceFile = getSourceFile ( filename ) ;
2039
- if ( sourceFile ) {
2040
- //
2041
- // If the sourceFile is the same, assume no update
2042
- //
2043
- if ( sourceFileUpToDate ( sourceFile ) ) {
2044
- continue ;
2016
+ function getOrCreateSourceFile ( filename : string ) : SourceFile {
2017
+ cancellationToken . throwIfCancellationRequested ( ) ;
2018
+
2019
+ // The program is asking for this file, check first if the host can locate it.
2020
+ // If the host can not locate the file, then it does not exist. return undefined
2021
+ // to the program to allow reporting of errors for missing files.
2022
+ var hostFileInformation = hostCache . getOrCreateEntry ( filename ) ;
2023
+ if ( ! hostFileInformation ) {
2024
+ return undefined ;
2025
+ }
2026
+
2027
+ // Check if the language version has changed since we last created a program; if they are the same,
2028
+ // it is safe to reuse the souceFiles; if not, then the shape of the AST can change, and the oldSourceFile
2029
+ // can not be reused. we have to dump all syntax trees and create new ones.
2030
+ if ( ! changesInCompilationSettingsAffectSyntax ) {
2031
+
2032
+ // Check if the old program had this file already
2033
+ var oldSourceFile = program && program . getSourceFile ( filename ) ;
2034
+ if ( oldSourceFile ) {
2035
+ // This SourceFile is safe to reuse, return it
2036
+ if ( sourceFileUpToDate ( oldSourceFile ) ) {
2037
+ return oldSourceFile ;
2038
+ }
2039
+
2040
+ // We have an older version of the sourceFile, incrementally parse the changes
2041
+ var textChangeRange = hostCache . getChangeRange ( filename , oldSourceFile . version , oldSourceFile . scriptSnapshot ) ;
2042
+ return documentRegistry . updateDocument ( oldSourceFile , filename , newSettings , hostFileInformation . scriptSnapshot , hostFileInformation . version , textChangeRange ) ;
2045
2043
}
2044
+ }
2045
+
2046
+ // Could not find this file in the old program, create a new SourceFile for it.
2047
+ return documentRegistry . acquireDocument ( filename , newSettings , hostFileInformation . scriptSnapshot , hostFileInformation . version ) ;
2048
+ }
2046
2049
2047
- var textChangeRange : TextChangeRange = null ;
2048
- textChangeRange = hostCache . getChangeRange ( filename , sourceFile . version , sourceFile . scriptSnapshot ) ;
2050
+ function sourceFileUpToDate ( sourceFile : SourceFile ) : boolean {
2051
+ return sourceFile && sourceFile . version === hostCache . getVersion ( sourceFile . filename ) ;
2052
+ }
2049
2053
2050
- sourceFile = documentRegistry . updateDocument ( sourceFile , filename , compilationSettings , scriptSnapshot , version , textChangeRange ) ;
2054
+ function programUpToDate ( ) : boolean {
2055
+ // If we haven't create a program yet, then it is not up-to-date
2056
+ if ( ! program ) {
2057
+ return false ;
2051
2058
}
2052
- else {
2053
- sourceFile = documentRegistry . acquireDocument ( filename , compilationSettings , scriptSnapshot , version ) ;
2059
+
2060
+ // If number of files in the program do not match, it is not up-to-date
2061
+ var rootFilenames = hostCache . getRootFilenames ( ) ;
2062
+ if ( program . getSourceFiles ( ) . length !== rootFilenames . length ) {
2063
+ return false ;
2054
2064
}
2055
2065
2056
- // Remember the new sourceFile
2057
- sourceFilesByName [ getCanonicalFileName ( filename ) ] = sourceFile ;
2058
- }
2066
+ // If any file is not up-to-date, then the whole program is not up-to-date
2067
+ for ( var i = 0 , n = rootFilenames . length ; i < n ; i ++ ) {
2068
+ if ( ! sourceFileUpToDate ( program . getSourceFile ( rootFilenames [ i ] ) ) ) {
2069
+ return false ;
2070
+ }
2071
+ }
2059
2072
2060
- // Now create a new compiler
2061
- program = createProgram ( hostfilenames , compilationSettings , {
2062
- getSourceFile,
2063
- getCancellationToken : ( ) => cancellationToken ,
2064
- getCanonicalFileName : filename => useCaseSensitivefilenames ? filename : filename . toLowerCase ( ) ,
2065
- useCaseSensitiveFileNames : ( ) => useCaseSensitivefilenames ,
2066
- getNewLine : ( ) => host . getNewLine ? host . getNewLine ( ) : "\r\n" ,
2067
- getDefaultLibFilename,
2068
- writeFile : ( filename , data , writeByteOrderMark ) => { } ,
2069
- getCurrentDirectory : ( ) => host . getCurrentDirectory ( )
2070
- } ) ;
2071
- typeInfoResolver = program . getTypeChecker ( /*produceDiagnostics*/ false ) ;
2073
+ // If the compilation settings do no match, then the program is not up-to-date
2074
+ return compareDataObjects ( program . getCompilerOptions ( ) , hostCache . compilationSettings ( ) ) ;
2075
+ }
2072
2076
}
2073
2077
2074
2078
/**
0 commit comments