@@ -7,52 +7,118 @@ namespace ts {
77 BuilderKindEmitAndSemanticDiagnostics
88 }
99
10- export function createBuilder ( host : BuilderHost , builderKind : BuilderKind . BuilderKindSemanticDiagnostics ) : SemanticDiagnosticsBuilder ;
11- export function createBuilder ( host : BuilderHost , builderKind : BuilderKind . BuilderKindEmitAndSemanticDiagnostics ) : EmitAndSemanticDiagnosticsBuilder ;
12- export function createBuilder ( host : BuilderHost , builderKind : BuilderKind ) {
13- /**
14- * State corresponding to all the file references and shapes of the module etc
15- */
16- const state = createBuilderStateOld ( {
17- useCaseSensitiveFileNames : host . useCaseSensitiveFileNames ( ) ,
18- createHash : host . createHash ,
19- onUpdateProgramInitialized,
20- onSourceFileAdd : addToChangedFilesSet ,
21- onSourceFileChanged : path => { addToChangedFilesSet ( path ) ; deleteSemanticDiagnostics ( path ) ; } ,
22- onSourceFileRemoved : deleteSemanticDiagnostics
23- } ) ;
24-
10+ interface BuilderStateWithChangedFiles extends BuilderState {
2511 /**
2612 * Cache of semantic diagnostics for files with their Path being the key
2713 */
28- const semanticDiagnosticsPerFile = createMap < ReadonlyArray < Diagnostic > > ( ) ;
29-
14+ semanticDiagnosticsPerFile : Map < ReadonlyArray < Diagnostic > > | undefined ;
3015 /**
3116 * The map has key by source file's path that has been changed
3217 */
33- const changedFilesSet = createMap < true > ( ) ;
34-
18+ changedFilesSet : Map < true > ;
3519 /**
3620 * Set of affected files being iterated
3721 */
38- let affectedFiles : ReadonlyArray < SourceFile > | undefined ;
22+ affectedFiles : ReadonlyArray < SourceFile > | undefined ;
3923 /**
4024 * Current index to retrieve affected file from
4125 */
42- let affectedFilesIndex = 0 ;
26+ affectedFilesIndex : number | undefined ;
4327 /**
4428 * Current changed file for iterating over affected files
4529 */
46- let currentChangedFilePath : Path | undefined ;
30+ currentChangedFilePath : Path | undefined ;
4731 /**
4832 * Map of file signatures, with key being file path, calculated while getting current changed file's affected files
4933 * These will be commited whenever the iteration through affected files of current changed file is complete
5034 */
51- const currentAffectedFilesSignatures = createMap < string > ( ) ;
35+ currentAffectedFilesSignatures : Map < string > | undefined ;
5236 /**
5337 * Already seen affected files
5438 */
55- const seenAffectedFiles = createMap < true > ( ) ;
39+ seenAffectedFiles : Map < true > | undefined ;
40+ }
41+
42+ function hasSameKeys < T , U > ( map1 : ReadonlyMap < T > | undefined , map2 : ReadonlyMap < U > | undefined ) {
43+ if ( map1 === undefined ) {
44+ return map2 === undefined ;
45+ }
46+ if ( map2 === undefined ) {
47+ return map1 === undefined ;
48+ }
49+ // Has same size and every key is present in both maps
50+ return map1 . size === map2 . size && ! forEachKey ( map1 , key => ! map2 . has ( key ) ) ;
51+ }
52+
53+ /**
54+ * Create the state so that we can iterate on changedFiles/affected files
55+ */
56+ function createBuilderStateWithChangedFiles ( newProgram : Program , getCanonicalFileName : GetCanonicalFileName , oldState ?: Readonly < BuilderStateWithChangedFiles > ) : BuilderStateWithChangedFiles {
57+ const state = BuilderState . create ( newProgram , getCanonicalFileName , oldState ) as BuilderStateWithChangedFiles ;
58+ const compilerOptions = newProgram . getCompilerOptions ( ) ;
59+ if ( ! compilerOptions . outFile && ! compilerOptions . out ) {
60+ state . semanticDiagnosticsPerFile = createMap < ReadonlyArray < Diagnostic > > ( ) ;
61+ }
62+ state . changedFilesSet = createMap < true > ( ) ;
63+ const useOldState = BuilderState . canReuseOldState ( state . referencedMap , oldState ) ;
64+ const canCopySemanticDiagnostics = useOldState && oldState . semanticDiagnosticsPerFile && ! ! state . semanticDiagnosticsPerFile ;
65+ if ( useOldState ) {
66+ // Verify the sanity of old state
67+ if ( ! oldState . currentChangedFilePath ) {
68+ Debug . assert ( ! oldState . affectedFiles && ( ! oldState . currentAffectedFilesSignatures || ! oldState . currentAffectedFilesSignatures . size ) , "Cannot reuse if only few affected files of currentChangedFile were iterated" ) ;
69+ }
70+ if ( canCopySemanticDiagnostics ) {
71+ Debug . assert ( ! forEachKey ( oldState . changedFilesSet , path => oldState . semanticDiagnosticsPerFile . has ( path ) ) , "Semantic diagnostics shouldnt be available for changed files" ) ;
72+ }
73+
74+ // Copy old state's changed files set
75+ copyEntries ( oldState . changedFilesSet , state . changedFilesSet ) ;
76+ }
77+
78+ // Update changed files and copy semantic diagnostics if we can
79+ const referencedMap = state . referencedMap ;
80+ const oldReferencedMap = useOldState && oldState . referencedMap ;
81+ state . fileInfos . forEach ( ( info , sourceFilePath ) => {
82+ let oldInfo : Readonly < BuilderState . FileInfo > ;
83+ let newReferences : BuilderState . ReferencedSet ;
84+
85+ // if not using old state, every file is changed
86+ if ( ! useOldState ||
87+ // File wasnt present in old state
88+ ! ( oldInfo = oldState . fileInfos . get ( sourceFilePath ) ) ||
89+ // versions dont match
90+ oldInfo . version !== info . version ||
91+ // Referenced files changed
92+ ! hasSameKeys ( newReferences = referencedMap && referencedMap . get ( sourceFilePath ) , oldReferencedMap && oldReferencedMap . get ( sourceFilePath ) ) ||
93+ // Referenced file was deleted in the new program
94+ newReferences && forEachKey ( newReferences , path => ! state . fileInfos . has ( path ) && oldState . fileInfos . has ( path ) ) ) {
95+ // Register file as changed file and do not copy semantic diagnostics, since all changed files need to be re-evaluated
96+ state . changedFilesSet . set ( sourceFilePath , true ) ;
97+ }
98+ else if ( canCopySemanticDiagnostics ) {
99+ // Unchanged file copy diagnostics
100+ const diagnostics = oldState . semanticDiagnosticsPerFile . get ( sourceFilePath ) ;
101+ if ( diagnostics ) {
102+ state . semanticDiagnosticsPerFile . set ( sourceFilePath , diagnostics ) ;
103+ }
104+ }
105+ } ) ;
106+
107+ return state ;
108+ }
109+
110+ export function createBuilder ( host : BuilderHost , builderKind : BuilderKind . BuilderKindSemanticDiagnostics ) : SemanticDiagnosticsBuilder ;
111+ export function createBuilder ( host : BuilderHost , builderKind : BuilderKind . BuilderKindEmitAndSemanticDiagnostics ) : EmitAndSemanticDiagnosticsBuilder ;
112+ export function createBuilder ( host : BuilderHost , builderKind : BuilderKind ) {
113+ /**
114+ * Create the canonical file name for identity
115+ */
116+ const getCanonicalFileName = createGetCanonicalFileName ( host . useCaseSensitiveFileNames ( ) ) ;
117+ /**
118+ * Computing hash to for signature verification
119+ */
120+ const computeHash = host . createHash || identity ;
121+ let state : BuilderStateWithChangedFiles ;
56122
57123 switch ( builderKind ) {
58124 case BuilderKind . BuilderKindSemanticDiagnostics :
@@ -81,55 +147,12 @@ namespace ts {
81147 } ;
82148 }
83149
84- /**
85- * Initialize changedFiles, affected files set, cached diagnostics, signatures
86- */
87- function onUpdateProgramInitialized ( isModuleEmitChanged : boolean ) {
88- if ( isModuleEmitChanged ) {
89- // Changes in the module emit, clear out everything and initialize as if first time
90-
91- // Clear file information and semantic diagnostics
92- semanticDiagnosticsPerFile . clear ( ) ;
93-
94- // Clear changed files and affected files information
95- changedFilesSet . clear ( ) ;
96- affectedFiles = undefined ;
97- currentChangedFilePath = undefined ;
98- currentAffectedFilesSignatures . clear ( ) ;
99- }
100- else {
101- if ( currentChangedFilePath ) {
102- // Remove the diagnostics for all the affected files since we should resume the state such that
103- // the whole iteration on currentChangedFile never happened
104- affectedFiles . forEach ( sourceFile => deleteSemanticDiagnostics ( sourceFile . path ) ) ;
105- affectedFiles = undefined ;
106- currentAffectedFilesSignatures . clear ( ) ;
107- }
108- else {
109- // Verify the sanity of old state
110- Debug . assert ( ! affectedFiles && ! currentAffectedFilesSignatures . size , "Cannot reuse if only few affected files of currentChangedFile were iterated" ) ;
111- }
112- Debug . assert ( ! forEachKey ( changedFilesSet , path => semanticDiagnosticsPerFile . has ( path ) ) , "Semantic diagnostics shouldnt be available for changed files" ) ;
113- }
114- }
115-
116- /**
117- * Add file to the changed files set
118- */
119- function addToChangedFilesSet ( path : Path ) {
120- changedFilesSet . set ( path , true ) ;
121- }
122-
123- function deleteSemanticDiagnostics ( path : Path ) {
124- semanticDiagnosticsPerFile . delete ( path ) ;
125- }
126-
127150 /**
128151 * Update current state to reflect new program
129152 * Updates changed files, references, file infos etc which happens through the state callbacks
130153 */
131154 function updateProgram ( newProgram : Program ) {
132- state . updateProgram ( newProgram ) ;
155+ state = createBuilderStateWithChangedFiles ( newProgram , getCanonicalFileName , state ) ;
133156 }
134157
135158 /**
@@ -140,11 +163,15 @@ namespace ts {
140163 */
141164 function getNextAffectedFile ( programOfThisState : Program , cancellationToken : CancellationToken | undefined ) : SourceFile | Program | undefined {
142165 while ( true ) {
166+ const { affectedFiles } = state ;
143167 if ( affectedFiles ) {
168+ const { seenAffectedFiles, semanticDiagnosticsPerFile } = state ;
169+ let { affectedFilesIndex } = state ;
144170 while ( affectedFilesIndex < affectedFiles . length ) {
145171 const affectedFile = affectedFiles [ affectedFilesIndex ] ;
146172 if ( ! seenAffectedFiles . has ( affectedFile . path ) ) {
147173 // Set the next affected file as seen and remove the cached semantic diagnostics
174+ state . affectedFilesIndex = affectedFilesIndex ;
148175 semanticDiagnosticsPerFile . delete ( affectedFile . path ) ;
149176 return affectedFile ;
150177 }
@@ -153,16 +180,16 @@ namespace ts {
153180 }
154181
155182 // Remove the changed file from the change set
156- changedFilesSet . delete ( currentChangedFilePath ) ;
157- currentChangedFilePath = undefined ;
183+ state . changedFilesSet . delete ( state . currentChangedFilePath ) ;
184+ state . currentChangedFilePath = undefined ;
158185 // Commit the changes in file signature
159- state . updateSignaturesFromCache ( currentAffectedFilesSignatures ) ;
160- currentAffectedFilesSignatures . clear ( ) ;
161- affectedFiles = undefined ;
186+ BuilderState . updateSignaturesFromCache ( state , state . currentAffectedFilesSignatures ) ;
187+ state . currentAffectedFilesSignatures . clear ( ) ;
188+ state . affectedFiles = undefined ;
162189 }
163190
164191 // Get next changed file
165- const nextKey = changedFilesSet . keys ( ) . next ( ) ;
192+ const nextKey = state . changedFilesSet . keys ( ) . next ( ) ;
166193 if ( nextKey . done ) {
167194 // Done
168195 return undefined ;
@@ -172,16 +199,17 @@ namespace ts {
172199 // With --out or --outFile all outputs go into single file
173200 // so operations are performed directly on program, return program
174201 if ( compilerOptions . outFile || compilerOptions . out ) {
175- Debug . assert ( semanticDiagnosticsPerFile . size === 0 ) ;
202+ Debug . assert ( ! state . semanticDiagnosticsPerFile ) ;
176203 return programOfThisState ;
177204 }
178205
179206 // Get next batch of affected files
180- currentAffectedFilesSignatures . clear ( ) ;
181- affectedFiles = state . getFilesAffectedBy ( programOfThisState , nextKey . value as Path , cancellationToken , currentAffectedFilesSignatures ) ;
182- currentChangedFilePath = nextKey . value as Path ;
183- semanticDiagnosticsPerFile . delete ( currentChangedFilePath ) ;
184- affectedFilesIndex = 0 ;
207+ state . currentAffectedFilesSignatures = state . currentAffectedFilesSignatures || createMap ( ) ;
208+ state . affectedFiles = BuilderState . getFilesAffectedBy ( state , programOfThisState , nextKey . value as Path , cancellationToken , computeHash , state . currentAffectedFilesSignatures ) ;
209+ state . currentChangedFilePath = nextKey . value as Path ;
210+ state . semanticDiagnosticsPerFile . delete ( nextKey . value as Path ) ;
211+ state . affectedFilesIndex = 0 ;
212+ state . seenAffectedFiles = state . seenAffectedFiles || createMap < true > ( ) ;
185213 }
186214 }
187215
@@ -191,11 +219,11 @@ namespace ts {
191219 */
192220 function doneWithAffectedFile ( programOfThisState : Program , affected : SourceFile | Program ) {
193221 if ( affected === programOfThisState ) {
194- changedFilesSet . clear ( ) ;
222+ state . changedFilesSet . clear ( ) ;
195223 }
196224 else {
197- seenAffectedFiles . set ( ( < SourceFile > affected ) . path , true ) ;
198- affectedFilesIndex ++ ;
225+ state . seenAffectedFiles . set ( ( affected as SourceFile ) . path , true ) ;
226+ state . affectedFilesIndex ++ ;
199227 }
200228 }
201229
@@ -277,10 +305,10 @@ namespace ts {
277305 * Note that it is assumed that the when asked about semantic diagnostics, the file has been taken out of affected files
278306 */
279307 function getSemanticDiagnostics ( programOfThisState : Program , sourceFile ?: SourceFile , cancellationToken ?: CancellationToken ) : ReadonlyArray < Diagnostic > {
280- Debug . assert ( ! affectedFiles || affectedFiles [ affectedFilesIndex - 1 ] !== sourceFile || ! semanticDiagnosticsPerFile . has ( sourceFile . path ) ) ;
308+ Debug . assert ( ! state . affectedFiles || state . affectedFiles [ state . affectedFilesIndex - 1 ] !== sourceFile || ! state . semanticDiagnosticsPerFile . has ( sourceFile . path ) ) ;
281309 const compilerOptions = programOfThisState . getCompilerOptions ( ) ;
282310 if ( compilerOptions . outFile || compilerOptions . out ) {
283- Debug . assert ( semanticDiagnosticsPerFile . size === 0 ) ;
311+ Debug . assert ( ! state . semanticDiagnosticsPerFile ) ;
284312 // We dont need to cache the diagnostics just return them from program
285313 return programOfThisState . getSemanticDiagnostics ( sourceFile , cancellationToken ) ;
286314 }
@@ -302,23 +330,23 @@ namespace ts {
302330 */
303331 function getSemanticDiagnosticsOfFile ( program : Program , sourceFile : SourceFile , cancellationToken ?: CancellationToken ) : ReadonlyArray < Diagnostic > {
304332 const path = sourceFile . path ;
305- const cachedDiagnostics = semanticDiagnosticsPerFile . get ( path ) ;
333+ const cachedDiagnostics = state . semanticDiagnosticsPerFile . get ( path ) ;
306334 // Report the semantic diagnostics from the cache if we already have those diagnostics present
307335 if ( cachedDiagnostics ) {
308336 return cachedDiagnostics ;
309337 }
310338
311339 // Diagnostics werent cached, get them from program, and cache the result
312340 const diagnostics = program . getSemanticDiagnostics ( sourceFile , cancellationToken ) ;
313- semanticDiagnosticsPerFile . set ( path , diagnostics ) ;
341+ state . semanticDiagnosticsPerFile . set ( path , diagnostics ) ;
314342 return diagnostics ;
315343 }
316344
317345 /**
318346 * Get all the dependencies of the sourceFile
319347 */
320348 function getAllDependencies ( programOfThisState : Program , sourceFile : SourceFile ) {
321- return state . getAllDependencies ( programOfThisState , sourceFile ) ;
349+ return BuilderState . getAllDependencies ( state , programOfThisState , sourceFile ) ;
322350 }
323351 }
324352}
0 commit comments