11/// <reference path="..\services\services.ts" />
2+ /// <reference path="utilities.ts"/>
23/// <reference path="scriptInfo.ts"/>
34/// <reference path="lshost.ts"/>
45
@@ -11,13 +12,33 @@ namespace ts.server {
1112
1213 export abstract class Project {
1314 private rootFiles : ScriptInfo [ ] = [ ] ;
14- private rootFilesMap : FileMap < ScriptInfo > = createFileMap < ScriptInfo > ( ) ;
15+ private readonly rootFilesMap : FileMap < ScriptInfo > = createFileMap < ScriptInfo > ( ) ;
1516 private lsHost : ServerLanguageServiceHost ;
16- protected program : ts . Program ;
17- private version = 0 ;
17+ private program : ts . Program ;
1818
1919 languageService : LanguageService ;
2020
21+ /**
22+ * Set of files that was returned from the last call to getChangesSinceVersion.
23+ */
24+ private lastReportedFileNames : Map < string > ;
25+ /**
26+ * Last version that was reported.
27+ */
28+ private lastReportedVersion = 0 ;
29+ /**
30+ * Current project structure version.
31+ * This property is changed in 'updateGraph' based on the set of files in program
32+ */
33+ private projectStructureVersion = 0 ;
34+ /**
35+ * Current version of the project state. It is changed when:
36+ * - new root file was added/removed
37+ * - edit happen in some file that is currently included in the project.
38+ * This property is different from projectStructureVersion since in most cases edits don't affect set of files in the project
39+ */
40+ private projectStateVersion = 0 ;
41+
2142 constructor (
2243 readonly projectKind : ProjectKind ,
2344 readonly projectService : ProjectService ,
@@ -46,7 +67,7 @@ namespace ts.server {
4667 }
4768
4869 getProjectVersion ( ) {
49- return this . version . toString ( ) ;
70+ return this . projectStateVersion . toString ( ) ;
5071 }
5172
5273 enableLanguageService ( ) {
@@ -68,8 +89,8 @@ namespace ts.server {
6889
6990 close ( ) {
7091 for ( const fileName of this . getFileNames ( ) ) {
71- const info = this . projectKind . getScriptInfoForNormalizedPath ( fileName ) ;
72- info . detachFromProject ( project ) ;
92+ const info = this . projectService . getScriptInfoForNormalizedPath ( fileName ) ;
93+ info . detachFromProject ( this ) ;
7394 }
7495 // signal language service to release files acquired from document registry
7596 this . languageService . dispose ( ) ;
@@ -85,6 +106,10 @@ namespace ts.server {
85106 }
86107
87108 getFileNames ( ) {
109+ if ( ! this . program ) {
110+ return [ ] ;
111+ }
112+
88113 if ( ! this . languageServiceEnabled ) {
89114 // if language service is disabled assume that all files in program are root files + default library
90115 let rootFiles = this . getRootFiles ( ) ;
@@ -128,16 +153,18 @@ namespace ts.server {
128153 }
129154 }
130155
131- removeFile ( info : ScriptInfo ) {
156+ removeFile ( info : ScriptInfo , detachFromProject : boolean = true ) {
132157 if ( ! this . removeRoot ( info ) ) {
133158 this . removeReferencedFile ( info )
134159 }
135- info . detachFromProject ( this ) ;
160+ if ( detachFromProject ) {
161+ info . detachFromProject ( this ) ;
162+ }
136163 this . markAsDirty ( ) ;
137164 }
138165
139166 markAsDirty ( ) {
140- this . version ++ ;
167+ this . projectStateVersion ++ ;
141168 }
142169
143170 // remove a root file from project
@@ -153,21 +180,36 @@ namespace ts.server {
153180
154181 private removeReferencedFile ( info : ScriptInfo ) {
155182 this . lsHost . removeReferencedFile ( info )
156- this . updateGraph ( ) ;
157183 }
158184
159185 updateGraph ( ) {
186+ if ( ! this . languageServiceEnabled ) {
187+ return ;
188+ }
189+
190+ const oldProgram = this . program ;
160191 this . program = this . languageService . getProgram ( ) ;
192+
193+ // bump up the version if
194+ // - oldProgram is not set - this is a first time updateGraph is called
195+ // - newProgram is different from the old program and structure of the old program was not reused.
196+ if ( ! oldProgram || ( this . program !== oldProgram && ! oldProgram . structureIsReused ) ) {
197+ this . projectStructureVersion ++ ;
198+ }
161199 }
162200
163- getScriptInfo ( uncheckedFileName : string ) {
164- const scriptInfo = this . projectService . getOrCreateScriptInfo ( toNormalizedPath ( uncheckedFileName ) , /*openedByClient*/ false ) ;
165- if ( scriptInfo . attachToProject ( this ) ) {
201+ getScriptInfoFromNormalizedPath ( fileName : NormalizedPath ) {
202+ const scriptInfo = this . projectService . getOrCreateScriptInfoForNormalizedPath ( fileName , /*openedByClient*/ false ) ;
203+ if ( scriptInfo && scriptInfo . attachToProject ( this ) ) {
166204 this . markAsDirty ( ) ;
167205 }
168206 return scriptInfo ;
169207 }
170208
209+ getScriptInfo ( uncheckedFileName : string ) {
210+ return this . getScriptInfoFromNormalizedPath ( toNormalizedPath ( uncheckedFileName ) ) ;
211+ }
212+
171213 filesToString ( ) {
172214 if ( ! this . program ) {
173215 return "" ;
@@ -197,74 +239,26 @@ namespace ts.server {
197239 }
198240 }
199241
200- reloadScript ( filename : string , tmpfilename : string , cb : ( ) => void ) {
201- const script = this . getScriptInfo ( filename ) ;
242+ reloadScript ( filename : NormalizedPath , cb : ( ) => void ) {
243+ const script = this . getScriptInfoFromNormalizedPath ( filename ) ;
202244 if ( script ) {
203245 script . reloadFromFile ( filename , cb ) ;
204246 }
205247 }
206- }
207-
208- export class InferredProject extends Project {
209248
210- static NextId = 0 ;
211-
212- readonly inferredProjectName ;
213- // Used to keep track of what directories are watched for this project
214- directoriesWatchedForTsconfig : string [ ] = [ ] ;
215-
216- constructor ( projectService : ProjectService , documentRegistry : ts . DocumentRegistry , languageServiceEnabled : boolean ) {
217- super ( ProjectKind . Inferred ,
218- projectService ,
219- documentRegistry ,
220- /*files*/ undefined ,
221- languageServiceEnabled ,
222- /*compilerOptions*/ undefined ) ;
223-
224- this . inferredProjectName = makeInferredProjectName ( InferredProject . NextId ++ ) ;
225- }
226-
227- getProjectName ( ) {
228- return this . inferredProjectName ;
229- }
230-
231- close ( ) {
232- super . close ( ) ;
233-
234- for ( const directory of this . directoriesWatchedForTsconfig ) {
235- this . projectService . stopWatchingDirectory ( directory ) ;
236- }
237- }
238- }
239-
240- export abstract class VersionedProject extends Project {
241-
242- private lastReportedFileNames : Map < string > ;
243- private lastReportedVersion : number = 0 ;
244- currentVersion : number = 1 ;
245-
246- updateGraph ( ) {
247- if ( ! this . languageServiceEnabled ) {
248- return ;
249- }
250- const oldProgram = this . program ;
251-
252- super . updateGraph ( ) ;
253-
254- if ( ! oldProgram || ! oldProgram . structureIsReused ) {
255- this . currentVersion ++ ;
256- }
257- }
258-
259- getChangesSinceVersion ( lastKnownVersion ?: number ) : protocol . ExternalProjectFiles {
249+ getChangesSinceVersion ( lastKnownVersion ?: number ) : protocol . ProjectFiles {
260250 const info = {
261251 projectName : this . getProjectName ( ) ,
262- version : this . currentVersion
252+ version : this . projectStructureVersion ,
253+ isInferred : this . projectKind === ProjectKind . Inferred
263254 } ;
255+ // check if requested version is the same that we have reported last time
264256 if ( this . lastReportedFileNames && lastKnownVersion === this . lastReportedVersion ) {
265- if ( this . currentVersion == this . lastReportedVersion ) {
257+ // if current structure version is the same - return info witout any changes
258+ if ( this . projectStructureVersion == this . lastReportedVersion ) {
266259 return { info } ;
267260 }
261+ // compute and return the difference
268262 const lastReportedFileNames = this . lastReportedFileNames ;
269263 const currentFiles = arrayToMap ( this . getFileNames ( ) , x => x ) ;
270264
@@ -283,27 +277,63 @@ namespace ts.server {
283277 this . lastReportedFileNames = currentFiles ;
284278
285279 this . lastReportedFileNames = currentFiles ;
286- this . lastReportedVersion = this . currentVersion ;
280+ this . lastReportedVersion = this . projectStructureVersion ;
287281 return { info, changes : { added, removed } } ;
288282 }
289283 else {
290284 // unknown version - return everything
291285 const projectFileNames = this . getFileNames ( ) ;
292286 this . lastReportedFileNames = arrayToMap ( projectFileNames , x => x ) ;
293- this . lastReportedVersion = this . currentVersion ;
287+ this . lastReportedVersion = this . projectStructureVersion ;
294288 return { info, files : projectFileNames } ;
295289 }
296290 }
297291 }
298292
299- export class ConfiguredProject extends VersionedProject {
293+ export class InferredProject extends Project {
294+
295+ private static NextId = 1 ;
296+
297+ /**
298+ * Unique name that identifies this particular inferred project
299+ */
300+ private readonly inferredProjectName : string ;
301+
302+ // Used to keep track of what directories are watched for this project
303+ directoriesWatchedForTsconfig : string [ ] = [ ] ;
304+
305+ constructor ( projectService : ProjectService , documentRegistry : ts . DocumentRegistry , languageServiceEnabled : boolean ) {
306+ super ( ProjectKind . Inferred ,
307+ projectService ,
308+ documentRegistry ,
309+ /*files*/ undefined ,
310+ languageServiceEnabled ,
311+ /*compilerOptions*/ undefined ) ;
312+
313+ this . inferredProjectName = makeInferredProjectName ( InferredProject . NextId ++ ) ;
314+ }
315+
316+ getProjectName ( ) {
317+ return this . inferredProjectName ;
318+ }
319+
320+ close ( ) {
321+ super . close ( ) ;
322+
323+ for ( const directory of this . directoriesWatchedForTsconfig ) {
324+ this . projectService . stopWatchingDirectory ( directory ) ;
325+ }
326+ }
327+ }
328+
329+ export class ConfiguredProject extends Project {
300330 private projectFileWatcher : FileWatcher ;
301331 private directoryWatcher : FileWatcher ;
302332 private directoriesWatchedForWildcards : Map < FileWatcher > ;
303333 /** Used for configured projects which may have multiple open roots */
304334 openRefCount = 0 ;
305335
306- constructor ( readonly configFileName : string ,
336+ constructor ( readonly configFileName : NormalizedPath ,
307337 projectService : ProjectService ,
308338 documentRegistry : ts . DocumentRegistry ,
309339 hasExplicitListOfFiles : boolean ,
@@ -380,7 +410,7 @@ namespace ts.server {
380410 }
381411 }
382412
383- export class ExternalProject extends VersionedProject {
413+ export class ExternalProject extends Project {
384414 constructor ( readonly externalProjectName : string ,
385415 projectService : ProjectService ,
386416 documentRegistry : ts . DocumentRegistry ,
0 commit comments