@@ -355,6 +355,10 @@ namespace ts.projectSystem {
355
355
return countWhere ( recursiveWatchedDirs , dir => file . length > dir . length && startsWith ( file , dir ) && file [ dir . length ] === directorySeparator ) ;
356
356
}
357
357
358
+ function checkOpenFiles ( projectService : server . ProjectService , expectedFiles : FileOrFolder [ ] ) {
359
+ checkFileNames ( "Open files" , projectService . openFiles . map ( info => info . fileName ) , expectedFiles . map ( file => file . path ) ) ;
360
+ }
361
+
358
362
/**
359
363
* Test server cancellation token used to mock host token cancellation requests.
360
364
* The cancelAfterRequest constructor param specifies how many isCancellationRequested() calls
@@ -1060,16 +1064,19 @@ namespace ts.projectSystem {
1060
1064
projectService . openClientFile ( file1 . path ) ;
1061
1065
checkNumberOfConfiguredProjects ( projectService , 1 ) ;
1062
1066
const project = projectService . configuredProjects . get ( configFile . path ) ;
1067
+ assert . isTrue ( project . hasOpenRef ( ) ) ; // file1
1063
1068
1064
1069
projectService . closeClientFile ( file1 . path ) ;
1065
1070
checkNumberOfConfiguredProjects ( projectService , 1 ) ;
1066
1071
assert . strictEqual ( projectService . configuredProjects . get ( configFile . path ) , project ) ;
1067
- assert . equal ( project . openRefCount , 0 ) ;
1072
+ assert . isFalse ( project . hasOpenRef ( ) ) ; // No open files
1073
+ assert . isFalse ( project . isClosed ( ) ) ;
1068
1074
1069
1075
projectService . openClientFile ( file2 . path ) ;
1070
1076
checkNumberOfConfiguredProjects ( projectService , 1 ) ;
1071
1077
assert . strictEqual ( projectService . configuredProjects . get ( configFile . path ) , project ) ;
1072
- assert . equal ( project . openRefCount , 1 ) ;
1078
+ assert . isTrue ( project . hasOpenRef ( ) ) ; // file2
1079
+ assert . isFalse ( project . isClosed ( ) ) ;
1073
1080
} ) ;
1074
1081
1075
1082
it ( "should not close configured project after closing last open file, but should be closed on next file open if its not the file from same project" , ( ) => {
@@ -1091,14 +1098,18 @@ namespace ts.projectSystem {
1091
1098
projectService . openClientFile ( file1 . path ) ;
1092
1099
checkNumberOfConfiguredProjects ( projectService , 1 ) ;
1093
1100
const project = projectService . configuredProjects . get ( configFile . path ) ;
1101
+ assert . isTrue ( project . hasOpenRef ( ) ) ; // file1
1094
1102
1095
1103
projectService . closeClientFile ( file1 . path ) ;
1096
1104
checkNumberOfConfiguredProjects ( projectService , 1 ) ;
1097
1105
assert . strictEqual ( projectService . configuredProjects . get ( configFile . path ) , project ) ;
1098
- assert . equal ( project . openRefCount , 0 ) ;
1106
+ assert . isFalse ( project . hasOpenRef ( ) ) ; // No files
1107
+ assert . isFalse ( project . isClosed ( ) ) ;
1099
1108
1100
1109
projectService . openClientFile ( libFile . path ) ;
1101
1110
checkNumberOfConfiguredProjects ( projectService , 0 ) ;
1111
+ assert . isFalse ( project . hasOpenRef ( ) ) ; // No files + project closed
1112
+ assert . isTrue ( project . isClosed ( ) ) ;
1102
1113
} ) ;
1103
1114
1104
1115
it ( "should not close external project with no open files" , ( ) => {
@@ -2085,39 +2096,224 @@ namespace ts.projectSystem {
2085
2096
projectService . openClientFile ( file2 . path ) ;
2086
2097
checkNumberOfProjects ( projectService , { configuredProjects : 1 } ) ;
2087
2098
const project1 = projectService . configuredProjects . get ( tsconfig1 . path ) ;
2088
- assert . equal ( project1 . openRefCount , 1 , "Open ref count in project1 - 1") ;
2099
+ assert . isTrue ( project1 . hasOpenRef ( ) , "Has open ref count in project1 - 1") ; // file2
2089
2100
assert . equal ( project1 . getScriptInfo ( file2 . path ) . containingProjects . length , 1 , "containing projects count" ) ;
2101
+ assert . isFalse ( project1 . isClosed ( ) ) ;
2090
2102
2091
2103
projectService . openClientFile ( file1 . path ) ;
2092
2104
checkNumberOfProjects ( projectService , { configuredProjects : 2 } ) ;
2093
- assert . equal ( project1 . openRefCount , 2 , "Open ref count in project1 - 2") ;
2105
+ assert . isTrue ( project1 . hasOpenRef ( ) , "Has open ref count in project1 - 2") ; // file2
2094
2106
assert . strictEqual ( projectService . configuredProjects . get ( tsconfig1 . path ) , project1 ) ;
2107
+ assert . isFalse ( project1 . isClosed ( ) ) ;
2095
2108
2096
2109
const project2 = projectService . configuredProjects . get ( tsconfig2 . path ) ;
2097
- assert . equal ( project2 . openRefCount , 1 , "Open ref count in project2 - 2" ) ;
2110
+ assert . isTrue ( project2 . hasOpenRef ( ) , "Has open ref count in project2 - 2" ) ; // file1
2111
+ assert . isFalse ( project2 . isClosed ( ) ) ;
2098
2112
2099
2113
assert . equal ( project1 . getScriptInfo ( file1 . path ) . containingProjects . length , 2 , `${ file1 . path } containing projects count` ) ;
2100
2114
assert . equal ( project1 . getScriptInfo ( file2 . path ) . containingProjects . length , 1 , `${ file2 . path } containing projects count` ) ;
2101
2115
2102
2116
projectService . closeClientFile ( file2 . path ) ;
2103
2117
checkNumberOfProjects ( projectService , { configuredProjects : 2 } ) ;
2104
- assert . equal ( project1 . openRefCount , 1 , "Open ref count in project1 - 3") ;
2105
- assert . equal ( project2 . openRefCount , 1 , "Open ref count in project2 - 3") ;
2118
+ assert . isFalse ( project1 . hasOpenRef ( ) , "Has open ref count in project1 - 3") ; // No files
2119
+ assert . isTrue ( project2 . hasOpenRef ( ) , "Has open ref count in project2 - 3") ; // file1
2106
2120
assert . strictEqual ( projectService . configuredProjects . get ( tsconfig1 . path ) , project1 ) ;
2107
2121
assert . strictEqual ( projectService . configuredProjects . get ( tsconfig2 . path ) , project2 ) ;
2122
+ assert . isFalse ( project1 . isClosed ( ) ) ;
2123
+ assert . isFalse ( project2 . isClosed ( ) ) ;
2108
2124
2109
2125
projectService . closeClientFile ( file1 . path ) ;
2110
2126
checkNumberOfProjects ( projectService , { configuredProjects : 2 } ) ;
2111
- assert . equal ( project1 . openRefCount , 0 , "Open ref count in project1 - 4") ;
2112
- assert . equal ( project2 . openRefCount , 0 , "Open ref count in project2 - 4") ;
2127
+ assert . isFalse ( project1 . hasOpenRef ( ) , "Has open ref count in project1 - 4") ; // No files
2128
+ assert . isFalse ( project2 . hasOpenRef ( ) , "Has open ref count in project2 - 4") ; // No files
2113
2129
assert . strictEqual ( projectService . configuredProjects . get ( tsconfig1 . path ) , project1 ) ;
2114
2130
assert . strictEqual ( projectService . configuredProjects . get ( tsconfig2 . path ) , project2 ) ;
2131
+ assert . isFalse ( project1 . isClosed ( ) ) ;
2132
+ assert . isFalse ( project2 . isClosed ( ) ) ;
2115
2133
2116
2134
projectService . openClientFile ( file2 . path ) ;
2117
2135
checkNumberOfProjects ( projectService , { configuredProjects : 1 } ) ;
2118
2136
assert . strictEqual ( projectService . configuredProjects . get ( tsconfig1 . path ) , project1 ) ;
2119
2137
assert . isUndefined ( projectService . configuredProjects . get ( tsconfig2 . path ) ) ;
2120
- assert . equal ( project1 . openRefCount , 1 , "Open ref count in project1 - 5" ) ;
2138
+ assert . isTrue ( project1 . hasOpenRef ( ) , "Has open ref count in project1 - 5" ) ; // file2
2139
+ assert . isFalse ( project1 . isClosed ( ) ) ;
2140
+ assert . isTrue ( project2 . isClosed ( ) ) ;
2141
+ } ) ;
2142
+
2143
+ it ( "Open ref of configured project when open file gets added to the project as part of configured file update" , ( ) => {
2144
+ const file1 : FileOrFolder = {
2145
+ path : "/a/b/src/file1.ts" ,
2146
+ content : "let x = 1;"
2147
+ } ;
2148
+ const file2 : FileOrFolder = {
2149
+ path : "/a/b/src/file2.ts" ,
2150
+ content : "let y = 1;"
2151
+ } ;
2152
+ const file3 : FileOrFolder = {
2153
+ path : "/a/b/file3.ts" ,
2154
+ content : "let z = 1;"
2155
+ } ;
2156
+ const file4 : FileOrFolder = {
2157
+ path : "/a/file4.ts" ,
2158
+ content : "let z = 1;"
2159
+ } ;
2160
+ const configFile = {
2161
+ path : "/a/b/tsconfig.json" ,
2162
+ content : JSON . stringify ( { files : [ "src/file1.ts" , "file3.ts" ] } )
2163
+ } ;
2164
+
2165
+ const files = [ file1 , file2 , file3 , file4 ] ;
2166
+ const host = createServerHost ( files . concat ( configFile ) ) ;
2167
+ const projectService = createProjectService ( host ) ;
2168
+
2169
+ projectService . openClientFile ( file1 . path ) ;
2170
+ projectService . openClientFile ( file2 . path ) ;
2171
+ projectService . openClientFile ( file3 . path ) ;
2172
+ projectService . openClientFile ( file4 . path ) ;
2173
+
2174
+ const infos = files . map ( file => projectService . getScriptInfoForPath ( file . path as Path ) ) ;
2175
+ checkOpenFiles ( projectService , files ) ;
2176
+ checkNumberOfProjects ( projectService , { configuredProjects : 1 , inferredProjects : 2 } ) ;
2177
+ const configProject1 = projectService . configuredProjects . get ( configFile . path ) ;
2178
+ assert . isTrue ( configProject1 . hasOpenRef ( ) ) ; // file1 and file3
2179
+ checkProjectActualFiles ( configProject1 , [ file1 . path , file3 . path , configFile . path ] ) ;
2180
+ const inferredProject1 = projectService . inferredProjects [ 0 ] ;
2181
+ checkProjectActualFiles ( inferredProject1 , [ file2 . path ] ) ;
2182
+ const inferredProject2 = projectService . inferredProjects [ 1 ] ;
2183
+ checkProjectActualFiles ( inferredProject2 , [ file4 . path ] ) ;
2184
+
2185
+ configFile . content = "{}" ;
2186
+ host . reloadFS ( files . concat ( configFile ) ) ;
2187
+ host . runQueuedTimeoutCallbacks ( ) ;
2188
+
2189
+ verifyScriptInfos ( ) ;
2190
+ checkOpenFiles ( projectService , files ) ;
2191
+ verifyConfiguredProjectStateAfterUpdate ( /*hasOpenRef*/ true ) ; // file1, file2, file3
2192
+ checkNumberOfInferredProjects ( projectService , 1 ) ;
2193
+ const inferredProject3 = projectService . inferredProjects [ 0 ] ;
2194
+ checkProjectActualFiles ( inferredProject3 , [ file4 . path ] ) ;
2195
+ assert . strictEqual ( inferredProject3 , inferredProject2 ) ;
2196
+
2197
+ projectService . closeClientFile ( file1 . path ) ;
2198
+ projectService . closeClientFile ( file2 . path ) ;
2199
+ projectService . closeClientFile ( file4 . path ) ;
2200
+
2201
+ verifyScriptInfos ( ) ;
2202
+ checkOpenFiles ( projectService , [ file3 ] ) ;
2203
+ verifyConfiguredProjectStateAfterUpdate ( /*hasOpenRef*/ true ) ; // file3
2204
+ checkNumberOfInferredProjects ( projectService , 0 ) ;
2205
+
2206
+ projectService . openClientFile ( file4 . path ) ;
2207
+ verifyScriptInfos ( ) ;
2208
+ checkOpenFiles ( projectService , [ file3 , file4 ] ) ;
2209
+ verifyConfiguredProjectStateAfterUpdate ( /*hasOpenRef*/ true ) ; // file3
2210
+ checkNumberOfInferredProjects ( projectService , 1 ) ;
2211
+ const inferredProject4 = projectService . inferredProjects [ 0 ] ;
2212
+ checkProjectActualFiles ( inferredProject4 , [ file4 . path ] ) ;
2213
+
2214
+ projectService . closeClientFile ( file3 . path ) ;
2215
+ verifyScriptInfos ( ) ;
2216
+ checkOpenFiles ( projectService , [ file4 ] ) ;
2217
+ verifyConfiguredProjectStateAfterUpdate ( /*hasOpenRef*/ false ) ; // No open files
2218
+ checkNumberOfInferredProjects ( projectService , 1 ) ;
2219
+ const inferredProject5 = projectService . inferredProjects [ 0 ] ;
2220
+ checkProjectActualFiles ( inferredProject4 , [ file4 . path ] ) ;
2221
+ assert . strictEqual ( inferredProject5 , inferredProject4 ) ;
2222
+
2223
+ const file5 : FileOrFolder = {
2224
+ path : "/file5.ts" ,
2225
+ content : "let zz = 1;"
2226
+ } ;
2227
+ host . reloadFS ( files . concat ( configFile , file5 ) ) ;
2228
+ projectService . openClientFile ( file5 . path ) ;
2229
+ verifyScriptInfosAreUndefined ( [ file1 , file2 , file3 ] ) ;
2230
+ assert . strictEqual ( projectService . getScriptInfoForPath ( file4 . path as Path ) , find ( infos , info => info . path === file4 . path ) ) ;
2231
+ assert . isDefined ( projectService . getScriptInfoForPath ( file5 . path as Path ) ) ;
2232
+ checkOpenFiles ( projectService , [ file4 , file5 ] ) ;
2233
+ checkNumberOfConfiguredProjects ( projectService , 0 ) ;
2234
+
2235
+ function verifyScriptInfos ( ) {
2236
+ infos . forEach ( info => assert . strictEqual ( projectService . getScriptInfoForPath ( info . path ) , info ) ) ;
2237
+ }
2238
+
2239
+ function verifyScriptInfosAreUndefined ( files : FileOrFolder [ ] ) {
2240
+ for ( const file of files ) {
2241
+ assert . isUndefined ( projectService . getScriptInfoForPath ( file . path as Path ) ) ;
2242
+ }
2243
+ }
2244
+
2245
+ function verifyConfiguredProjectStateAfterUpdate ( hasOpenRef : boolean ) {
2246
+ checkNumberOfConfiguredProjects ( projectService , 1 ) ;
2247
+ const configProject2 = projectService . configuredProjects . get ( configFile . path ) ;
2248
+ assert . strictEqual ( configProject2 , configProject1 ) ;
2249
+ checkProjectActualFiles ( configProject2 , [ file1 . path , file2 . path , file3 . path , configFile . path ] ) ;
2250
+ assert . equal ( configProject2 . hasOpenRef ( ) , hasOpenRef ) ;
2251
+ }
2252
+ } ) ;
2253
+
2254
+ it ( "Open ref of configured project when open file gets added to the project as part of configured file update buts its open file references are all closed when the update happens" , ( ) => {
2255
+ const file1 : FileOrFolder = {
2256
+ path : "/a/b/src/file1.ts" ,
2257
+ content : "let x = 1;"
2258
+ } ;
2259
+ const file2 : FileOrFolder = {
2260
+ path : "/a/b/src/file2.ts" ,
2261
+ content : "let y = 1;"
2262
+ } ;
2263
+ const file3 : FileOrFolder = {
2264
+ path : "/a/b/file3.ts" ,
2265
+ content : "let z = 1;"
2266
+ } ;
2267
+ const file4 : FileOrFolder = {
2268
+ path : "/a/file4.ts" ,
2269
+ content : "let z = 1;"
2270
+ } ;
2271
+ const configFile = {
2272
+ path : "/a/b/tsconfig.json" ,
2273
+ content : JSON . stringify ( { files : [ "src/file1.ts" , "file3.ts" ] } )
2274
+ } ;
2275
+
2276
+ const files = [ file1 , file2 , file3 ] ;
2277
+ const hostFiles = files . concat ( file4 , configFile ) ;
2278
+ const host = createServerHost ( hostFiles ) ;
2279
+ const projectService = createProjectService ( host ) ;
2280
+
2281
+ projectService . openClientFile ( file1 . path ) ;
2282
+ projectService . openClientFile ( file2 . path ) ;
2283
+ projectService . openClientFile ( file3 . path ) ;
2284
+
2285
+ checkNumberOfProjects ( projectService , { configuredProjects : 1 , inferredProjects : 1 } ) ;
2286
+ const configuredProject = projectService . configuredProjects . get ( configFile . path ) ;
2287
+ assert . isTrue ( configuredProject . hasOpenRef ( ) ) ; // file1 and file3
2288
+ checkProjectActualFiles ( configuredProject , [ file1 . path , file3 . path , configFile . path ] ) ;
2289
+ const inferredProject1 = projectService . inferredProjects [ 0 ] ;
2290
+ checkProjectActualFiles ( inferredProject1 , [ file2 . path ] ) ;
2291
+
2292
+ projectService . closeClientFile ( file1 . path ) ;
2293
+ projectService . closeClientFile ( file3 . path ) ;
2294
+ assert . isFalse ( configuredProject . hasOpenRef ( ) ) ; // No files
2295
+
2296
+ configFile . content = "{}" ;
2297
+ host . reloadFS ( files . concat ( configFile ) ) ;
2298
+ // Time out is not yet run so there is project update pending
2299
+ assert . isTrue ( configuredProject . hasOpenRef ( ) ) ; // Pending update and file2 might get into the project
2300
+
2301
+ projectService . openClientFile ( file4 . path ) ;
2302
+
2303
+ checkNumberOfProjects ( projectService , { configuredProjects : 1 , inferredProjects : 2 } ) ;
2304
+ assert . strictEqual ( projectService . configuredProjects . get ( configFile . path ) , configuredProject ) ;
2305
+ assert . isTrue ( configuredProject . hasOpenRef ( ) ) ; // Pending update and F2 might get into the project
2306
+ assert . strictEqual ( projectService . inferredProjects [ 0 ] , inferredProject1 ) ;
2307
+ const inferredProject2 = projectService . inferredProjects [ 1 ] ;
2308
+ checkProjectActualFiles ( inferredProject2 , [ file4 . path ] ) ;
2309
+
2310
+ host . runQueuedTimeoutCallbacks ( ) ;
2311
+ checkNumberOfProjects ( projectService , { configuredProjects : 1 , inferredProjects : 1 } ) ;
2312
+ assert . strictEqual ( projectService . configuredProjects . get ( configFile . path ) , configuredProject ) ;
2313
+ assert . isTrue ( configuredProject . hasOpenRef ( ) ) ; // file2
2314
+ checkProjectActualFiles ( configuredProject , [ file1 . path , file2 . path , file3 . path , configFile . path ] ) ;
2315
+ assert . strictEqual ( projectService . inferredProjects [ 0 ] , inferredProject2 ) ;
2316
+ checkProjectActualFiles ( inferredProject2 , [ file4 . path ] ) ;
2121
2317
} ) ;
2122
2318
2123
2319
it ( "language service disabled state is updated in external projects" , ( ) => {
@@ -2188,18 +2384,36 @@ namespace ts.projectSystem {
2188
2384
projectService . openClientFile ( f1 . path ) ;
2189
2385
projectService . checkNumberOfProjects ( { configuredProjects : 1 } ) ;
2190
2386
const project = projectService . configuredProjects . get ( config . path ) ;
2387
+ assert . isTrue ( project . hasOpenRef ( ) ) ; // f1
2388
+ assert . isFalse ( project . isClosed ( ) ) ;
2191
2389
2192
2390
projectService . closeClientFile ( f1 . path ) ;
2193
2391
projectService . checkNumberOfProjects ( { configuredProjects : 1 } ) ;
2194
2392
assert . strictEqual ( projectService . configuredProjects . get ( config . path ) , project ) ;
2195
- assert . equal ( project . openRefCount , 0 ) ;
2393
+ assert . isFalse ( project . hasOpenRef ( ) ) ; // No files
2394
+ assert . isFalse ( project . isClosed ( ) ) ;
2196
2395
2197
2396
for ( const f of [ f1 , f2 , f3 ] ) {
2198
- // There shouldnt be any script info as we closed the file that resulted in creation of it
2397
+ // All the script infos should be present and contain the project since it is still alive.
2199
2398
const scriptInfo = projectService . getScriptInfoForNormalizedPath ( server . toNormalizedPath ( f . path ) ) ;
2200
2399
assert . equal ( scriptInfo . containingProjects . length , 1 , `expect 1 containing projects for '${ f . path } '` ) ;
2201
2400
assert . equal ( scriptInfo . containingProjects [ 0 ] , project , `expect configured project to be the only containing project for '${ f . path } '` ) ;
2202
2401
}
2402
+
2403
+ const f4 = {
2404
+ path : "/aa.js" ,
2405
+ content : "var x = 1"
2406
+ } ;
2407
+ host . reloadFS ( [ f1 , f2 , f3 , config , f4 ] ) ;
2408
+ projectService . openClientFile ( f4 . path ) ;
2409
+ projectService . checkNumberOfProjects ( { inferredProjects : 1 } ) ;
2410
+ assert . isFalse ( project . hasOpenRef ( ) ) ; // No files
2411
+ assert . isTrue ( project . isClosed ( ) ) ;
2412
+
2413
+ for ( const f of [ f1 , f2 , f3 ] ) {
2414
+ // All the script infos should not be present since the project is closed and orphan script infos are collected
2415
+ assert . isUndefined ( projectService . getScriptInfoForNormalizedPath ( server . toNormalizedPath ( f . path ) ) ) ;
2416
+ }
2203
2417
} ) ;
2204
2418
2205
2419
it ( "language service disabled events are triggered" , ( ) => {
@@ -2836,17 +3050,19 @@ namespace ts.projectSystem {
2836
3050
projectService . openClientFile ( f . path ) ;
2837
3051
projectService . checkNumberOfProjects ( { configuredProjects : 1 } ) ;
2838
3052
const project = projectService . configuredProjects . get ( config . path ) ;
2839
- assert . equal ( project . openRefCount , 1 ) ;
3053
+ assert . isTrue ( project . hasOpenRef ( ) ) ; // f
2840
3054
2841
3055
projectService . closeClientFile ( f . path ) ;
2842
3056
projectService . checkNumberOfProjects ( { configuredProjects : 1 } ) ;
2843
3057
assert . strictEqual ( projectService . configuredProjects . get ( config . path ) , project ) ;
2844
- assert . equal ( project . openRefCount , 0 ) ;
3058
+ assert . isFalse ( project . hasOpenRef ( ) ) ; // No files
3059
+ assert . isFalse ( project . isClosed ( ) ) ;
2845
3060
2846
3061
projectService . openClientFile ( f . path ) ;
2847
3062
projectService . checkNumberOfProjects ( { configuredProjects : 1 } ) ;
2848
3063
assert . strictEqual ( projectService . configuredProjects . get ( config . path ) , project ) ;
2849
- assert . equal ( project . openRefCount , 1 ) ;
3064
+ assert . isTrue ( project . hasOpenRef ( ) ) ; // f
3065
+ assert . isFalse ( project . isClosed ( ) ) ;
2850
3066
} ) ;
2851
3067
} ) ;
2852
3068
0 commit comments