@@ -81,10 +81,10 @@ module ts {
81
81
function reportDiagnostic ( error : Diagnostic ) {
82
82
if ( error . file ) {
83
83
var loc = error . file . getLineAndCharacterFromPosition ( error . start ) ;
84
- sys . writeErr ( error . file . filename + "(" + loc . line + "," + loc . character + "): " + error . messageText + sys . newLine ) ;
84
+ sys . write ( error . file . filename + "(" + loc . line + "," + loc . character + "): " + error . messageText + sys . newLine ) ;
85
85
}
86
86
else {
87
- sys . writeErr ( error . messageText + sys . newLine ) ;
87
+ sys . write ( error . messageText + sys . newLine ) ;
88
88
}
89
89
}
90
90
@@ -110,7 +110,7 @@ module ts {
110
110
}
111
111
112
112
function reportStatisticalValue ( name : string , value : string ) {
113
- sys . writeErr ( padRight ( name + ":" , 12 ) + padLeft ( value . toString ( ) , 10 ) + sys . newLine ) ;
113
+ sys . write ( padRight ( name + ":" , 12 ) + padLeft ( value . toString ( ) , 10 ) + sys . newLine ) ;
114
114
}
115
115
116
116
function reportCountStatistic ( name : string , count : number ) {
@@ -179,33 +179,133 @@ module ts {
179
179
} ;
180
180
}
181
181
182
- export function executeCommandLine ( args : string [ ] ) : number {
183
- var cmds = parseCommandLine ( args ) ;
182
+ export function executeCommandLine ( args : string [ ] ) : void {
183
+ var commandLine = parseCommandLine ( args ) ;
184
184
185
- if ( cmds . options . locale ) {
186
- validateLocaleAndSetLanguage ( cmds . options . locale , cmds . errors ) ;
185
+ if ( commandLine . options . locale ) {
186
+ validateLocaleAndSetLanguage ( commandLine . options . locale , commandLine . errors ) ;
187
187
}
188
188
189
- if ( cmds . filenames . length === 0 && ! ( cmds . options . help || cmds . options . version ) ) {
190
- cmds . errors . push ( createCompilerDiagnostic ( Diagnostics . No_input_files_specified ) ) ;
191
- }
192
-
193
- if ( cmds . options . version ) {
189
+ if ( commandLine . options . version ) {
194
190
reportDiagnostic ( createCompilerDiagnostic ( Diagnostics . Version_0 , version ) ) ;
195
- return 0 ;
191
+ sys . exit ( 0 ) ;
196
192
}
197
193
198
- if ( cmds . filenames . length === 0 || cmds . options . help ) {
194
+ if ( commandLine . options . help ) {
199
195
// TODO (drosen): Usage.
196
+ sys . exit ( 0 ) ;
197
+ }
198
+
199
+ if ( commandLine . filenames . length === 0 ) {
200
+ commandLine . errors . push ( createCompilerDiagnostic ( Diagnostics . No_input_files_specified ) ) ;
201
+ }
202
+
203
+ if ( commandLine . errors . length ) {
204
+ reportDiagnostics ( commandLine . errors ) ;
205
+ sys . exit ( 1 ) ;
206
+ }
207
+
208
+ var defaultCompilerHost = createCompilerHost ( commandLine . options ) ;
209
+
210
+ if ( commandLine . options . watch ) {
211
+ if ( ! sys . watchFile ) {
212
+ reportDiagnostic ( createCompilerDiagnostic ( Diagnostics . The_current_host_does_not_support_the_0_option , "--watch" ) ) ;
213
+ sys . exit ( 1 ) ;
214
+ }
215
+
216
+ watchProgram ( commandLine , defaultCompilerHost ) ;
217
+ }
218
+ else {
219
+ sys . exit ( compile ( commandLine , defaultCompilerHost ) . errors . length > 0 ? 1 : 0 ) ;
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Compiles the program once, and then watches all given and referenced files for changes.
225
+ * Upon detecting a file change, watchProgram will queue up file modification events for the next
226
+ * 250ms and then perform a recompilation. The reasoning is that in some cases, an editor can
227
+ * save all files at once, and we'd like to just perform a single recompilation.
228
+ */
229
+ function watchProgram ( commandLine : ParsedCommandLine , compilerHost : CompilerHost ) : void {
230
+ var watchers : Map < FileWatcher > = { } ;
231
+ var updatedFiles : Map < boolean > = { } ;
232
+
233
+ // Compile the program the first time and watch all given/referenced files.
234
+ var program = compile ( commandLine , compilerHost ) . program ;
235
+ reportDiagnostic ( createCompilerDiagnostic ( Diagnostics . Compilation_complete_Watching_for_file_changes ) ) ;
236
+ addWatchers ( program ) ;
237
+ return ;
238
+
239
+ function addWatchers ( program : Program ) {
240
+ forEach ( program . getSourceFiles ( ) , f => {
241
+ var filename = f . filename ;
242
+ watchers [ filename ] = sys . watchFile ( filename , fileUpdated ) ;
243
+ } ) ;
200
244
}
201
245
202
- if ( cmds . errors . length ) {
203
- reportDiagnostics ( cmds . errors ) ;
204
- return 1 ;
246
+ function removeWatchers ( program : Program ) {
247
+ forEach ( program . getSourceFiles ( ) , f => {
248
+ var filename = f . filename ;
249
+ if ( hasProperty ( watchers , filename ) ) {
250
+ watchers [ filename ] . close ( ) ;
251
+ }
252
+ } ) ;
253
+
254
+ watchers = { } ;
255
+ }
256
+
257
+ // Fired off whenever a file is changed.
258
+ function fileUpdated ( filename : string ) {
259
+ var firstNotification = isEmpty ( updatedFiles ) ;
260
+
261
+ updatedFiles [ filename ] = true ;
262
+
263
+ // Only start this off when the first file change comes in,
264
+ // so that we can batch up all further changes.
265
+ if ( firstNotification ) {
266
+ setTimeout ( ( ) => {
267
+ var changedFiles = updatedFiles ;
268
+ updatedFiles = { } ;
269
+
270
+ recompile ( changedFiles ) ;
271
+ } , 250 ) ;
272
+ }
205
273
}
206
274
275
+ function recompile ( changedFiles : Map < boolean > ) {
276
+ reportDiagnostic ( createCompilerDiagnostic ( Diagnostics . File_change_detected_Compiling ) ) ;
277
+ // Remove all the watchers, as we may not be watching every file
278
+ // specified since the last compilation cycle.
279
+ removeWatchers ( program ) ;
280
+
281
+ // Gets us syntactically correct files from the last compilation.
282
+ var getUnmodifiedSourceFile = program . getSourceFile ;
283
+
284
+ // We create a new compiler host for this compilation cycle.
285
+ // This new host is effectively the same except that 'getSourceFile'
286
+ // will try to reuse the SourceFiles from the last compilation cycle
287
+ // so long as they were not modified.
288
+ var newCompilerHost = clone ( compilerHost ) ;
289
+ newCompilerHost . getSourceFile = ( fileName , languageVersion , onError ) => {
290
+ if ( ! hasProperty ( changedFiles , fileName ) ) {
291
+ var sourceFile = getUnmodifiedSourceFile ( fileName ) ;
292
+ if ( sourceFile ) {
293
+ return sourceFile ;
294
+ }
295
+ }
296
+
297
+ return compilerHost . getSourceFile ( fileName , languageVersion , onError ) ;
298
+ } ;
299
+
300
+ program = compile ( commandLine , newCompilerHost ) . program ;
301
+ reportDiagnostic ( createCompilerDiagnostic ( Diagnostics . Compilation_complete_Watching_for_file_changes ) ) ;
302
+ addWatchers ( program ) ;
303
+ }
304
+ }
305
+
306
+ function compile ( commandLine : ParsedCommandLine , compilerHost : CompilerHost ) {
207
307
var parseStart = new Date ( ) . getTime ( ) ;
208
- var program = createProgram ( cmds . filenames , cmds . options , createCompilerHost ( cmds . options ) ) ;
308
+ var program = createProgram ( commandLine . filenames , commandLine . options , compilerHost ) ;
209
309
var bindStart = new Date ( ) . getTime ( ) ;
210
310
var errors = program . getDiagnostics ( ) ;
211
311
if ( errors . length ) {
@@ -224,7 +324,7 @@ module ts {
224
324
}
225
325
226
326
reportDiagnostics ( errors ) ;
227
- if ( cmds . options . diagnostics ) {
327
+ if ( commandLine . options . diagnostics ) {
228
328
reportCountStatistic ( "Files" , program . getSourceFiles ( ) . length ) ;
229
329
reportCountStatistic ( "Lines" , countLines ( program ) ) ;
230
330
reportCountStatistic ( "Nodes" , checker ? checker . getNodeCount ( ) : 0 ) ;
@@ -237,8 +337,10 @@ module ts {
237
337
reportTimeStatistic ( "Emit time" , reportStart - emitStart ) ;
238
338
reportTimeStatistic ( "Total time" , reportStart - parseStart ) ;
239
339
}
240
- return errors . length ? 1 : 0 ;
340
+
341
+ return { program : program , errors : errors } ;
342
+
241
343
}
242
344
}
243
345
244
- sys . exit ( ts . executeCommandLine ( sys . args ) ) ;
346
+ ts . executeCommandLine ( sys . args ) ;
0 commit comments