@@ -32,10 +32,10 @@ namespace ts {
3232 }
3333
3434 /* @internal */
35- export enum WatchPriority {
36- High ,
37- Medium ,
38- Low
35+ export enum PollingInterval {
36+ High = 2000 ,
37+ Medium = 500 ,
38+ Low = 250
3939 }
4040
4141 function getPriorityValues ( highPriorityValue : number ) : [ number , number , number ] {
@@ -44,36 +44,47 @@ namespace ts {
4444 return [ highPriorityValue , mediumPriorityValue , lowPriorityValue ] ;
4545 }
4646
47- const pollingIntervalsForPriority = getPriorityValues ( 250 ) ;
48- function pollingInterval ( watchPriority : WatchPriority ) : number {
47+ function pollingInterval ( watchPriority : PollingInterval ) : number {
4948 return pollingIntervalsForPriority [ watchPriority ] ;
5049 }
5150
51+ const pollingIntervalsForPriority = getPriorityValues ( 250 ) ;
52+
5253 /* @internal */
53- export function watchFileUsingPriorityPollingInterval ( host : System , fileName : string , callback : FileWatcherCallback , watchPriority : WatchPriority ) : FileWatcher {
54+ export function watchFileUsingPriorityPollingInterval ( host : System , fileName : string , callback : FileWatcherCallback , watchPriority : PollingInterval ) : FileWatcher {
5455 return host . watchFile ( fileName , callback , pollingInterval ( watchPriority ) ) ;
5556 }
5657
5758 /* @internal */
58- export interface DynamicPriorityPollingStatsSet {
59- watchFile ( fileName : string , callback : FileWatcherCallback , defaultPriority : WatchPriority ) : FileWatcher ;
60- }
59+ export type HostWatchFile = ( fileName : string , callback : FileWatcherCallback , pollingInterval : PollingInterval ) => FileWatcher ;
6160
6261 /* @internal */
6362 export const missingFileModifiedTime = new Date ( 0 ) ; // Any subsequent modification will occur after this time
6463
65- const chunkSizeOrUnchangedThresholdsForPriority = getPriorityValues ( 32 ) ;
66- function chunkSize ( watchPriority : WatchPriority ) {
67- return chunkSizeOrUnchangedThresholdsForPriority [ watchPriority ] ;
64+ enum ChunkSize {
65+ Low = 32 ,
66+ Medium = 64 ,
67+ High = 256
68+ }
69+
70+ function chunkSize ( pollingInterval : PollingInterval ) {
71+ switch ( pollingInterval ) {
72+ case PollingInterval . Low :
73+ return ChunkSize . Low ;
74+ case PollingInterval . Medium :
75+ return ChunkSize . Medium ;
76+ case PollingInterval . High :
77+ return ChunkSize . High ;
78+ }
6879 }
6980
7081 /*@internal */
71- export function unChangedThreshold ( watchPriority : WatchPriority ) {
72- return chunkSizeOrUnchangedThresholdsForPriority [ watchPriority ] ;
82+ export function unChangedThreshold ( pollingInterval : PollingInterval ) {
83+ return chunkSize ( pollingInterval ) ;
7384 }
7485
7586 /* @internal */
76- export function createDynamicPriorityPollingStatsSet ( host : System ) : DynamicPriorityPollingStatsSet {
87+ export function createDynamicPriorityPollingWatchFile ( host : System ) : HostWatchFile {
7788 if ( ! host . getModifiedTime || ! host . setTimeout ) {
7889 throw notImplemented ( ) ;
7990 }
@@ -83,20 +94,20 @@ namespace ts {
8394 unchangedPolls : number ;
8495 }
8596
86- interface WatchPriorityQueue extends Array < WatchedFile > {
87- watchPriority : WatchPriority ;
97+ interface PollingIntervalQueue extends Array < WatchedFile > {
98+ pollingInterval : PollingInterval ;
8899 pollIndex : number ;
89100 pollScheduled : boolean ;
90101 }
91102
92103 const watchedFiles : WatchedFile [ ] = [ ] ;
93104 const changedFilesInLastPoll : WatchedFile [ ] = [ ] ;
94- const priorityQueues = [ createPriorityQueue ( WatchPriority . High ) , createPriorityQueue ( WatchPriority . Medium ) , createPriorityQueue ( WatchPriority . Low ) ] ;
95- return {
96- watchFile
97- } ;
105+ const lowPollingIntervalQueue = createPollingIntervalQueue ( PollingInterval . Low ) ;
106+ const mediumPollingIntervalQueue = createPollingIntervalQueue ( PollingInterval . Medium ) ;
107+ const highPollingIntervalQueue = createPollingIntervalQueue ( PollingInterval . High ) ;
108+ return watchFile ;
98109
99- function watchFile ( fileName : string , callback : FileWatcherCallback , defaultPriority : WatchPriority ) : FileWatcher {
110+ function watchFile ( fileName : string , callback : FileWatcherCallback , defaultPollingInterval : PollingInterval ) : FileWatcher {
100111 const file : WatchedFile = {
101112 fileName,
102113 callback,
@@ -105,52 +116,51 @@ namespace ts {
105116 } ;
106117 watchedFiles . push ( file ) ;
107118
108- addToPriorityQueue ( file , defaultPriority ) ;
119+ addToPollingIntervalQueue ( file , defaultPollingInterval ) ;
109120 return {
110121 close : ( ) => {
111122 file . isClosed = true ;
112123 // Remove from watchedFiles
113124 unorderedRemoveItem ( watchedFiles , file ) ;
114- // Do not update priority queue since that will happen as part of polling
125+ // Do not update polling interval queue since that will happen as part of polling
115126 }
116127 } ;
117128 }
118129
119- function createPriorityQueue ( watchPriority : WatchPriority ) : WatchPriorityQueue {
120- const queue = [ ] as WatchPriorityQueue ;
121- queue . watchPriority = watchPriority ;
130+ function createPollingIntervalQueue ( pollingInterval : PollingInterval ) : PollingIntervalQueue {
131+ const queue = [ ] as PollingIntervalQueue ;
132+ queue . pollingInterval = pollingInterval ;
122133 queue . pollIndex = 0 ;
123134 queue . pollScheduled = false ;
124135 return queue ;
125136 }
126137
127- function pollPriorityQueue ( queue : WatchPriorityQueue ) {
128- const priority = queue . watchPriority ;
129- queue . pollIndex = pollQueue ( queue , priority , queue . pollIndex , chunkSize ( priority ) ) ;
138+ function pollPollingIntervalQueue ( queue : PollingIntervalQueue ) {
139+ queue . pollIndex = pollQueue ( queue , queue . pollingInterval , queue . pollIndex , chunkSize ( queue . pollingInterval ) ) ;
130140 // Set the next polling index and timeout
131141 if ( queue . length ) {
132- scheduleNextPoll ( priority ) ;
142+ scheduleNextPoll ( queue . pollingInterval ) ;
133143 }
134144 else {
135145 Debug . assert ( queue . pollIndex === 0 ) ;
136146 queue . pollScheduled = false ;
137147 }
138148 }
139149
140- function pollHighPriorityQueue ( queue : WatchPriorityQueue ) {
150+ function pollLowPollingIntervalQueue ( queue : PollingIntervalQueue ) {
141151 // Always poll complete list of changedFilesInLastPoll
142- pollQueue ( changedFilesInLastPoll , WatchPriority . High , /*pollIndex*/ 0 , changedFilesInLastPoll . length ) ;
152+ pollQueue ( changedFilesInLastPoll , PollingInterval . Low , /*pollIndex*/ 0 , changedFilesInLastPoll . length ) ;
143153
144154 // Finally do the actual polling of the queue
145- pollPriorityQueue ( queue ) ;
155+ pollPollingIntervalQueue ( queue ) ;
146156 // Schedule poll if there are files in changedFilesInLastPoll but no files in the actual queue
147- // as pollPriorityQueue wont schedule for next poll
157+ // as pollPollingIntervalQueue wont schedule for next poll
148158 if ( ! queue . pollScheduled && changedFilesInLastPoll . length ) {
149- scheduleNextPoll ( WatchPriority . High ) ;
159+ scheduleNextPoll ( PollingInterval . Low ) ;
150160 }
151161 }
152162
153- function pollQueue ( queue : WatchedFile [ ] , priority : WatchPriority , pollIndex : number , chunkSize : number ) {
163+ function pollQueue ( queue : WatchedFile [ ] , pollingInterval : PollingInterval , pollIndex : number , chunkSize : number ) {
154164 // Max visit would be all elements of the queue
155165 let needsVisit = queue . length ;
156166 let definedValueCopyToIndex = pollIndex ;
@@ -175,22 +185,22 @@ namespace ts {
175185 // Changed files go to changedFilesInLastPoll queue
176186 if ( queue !== changedFilesInLastPoll ) {
177187 queue [ pollIndex ] = undefined ;
178- addChangedFileToHighPriorityQueue ( watchedFile ) ;
188+ addChangedFileToLowPollingIntervalQueue ( watchedFile ) ;
179189 }
180190 }
181- else if ( watchedFile . unchangedPolls !== unChangedThreshold ( priority ) ) {
191+ else if ( watchedFile . unchangedPolls !== unChangedThreshold ( pollingInterval ) ) {
182192 watchedFile . unchangedPolls ++ ;
183193 }
184194 else if ( queue === changedFilesInLastPoll ) {
185- // Restart unchangedPollCount for unchanged file and move to high priority queue
195+ // Restart unchangedPollCount for unchanged file and move to low polling interval queue
186196 watchedFile . unchangedPolls = 1 ;
187197 queue [ pollIndex ] = undefined ;
188- addToPriorityQueue ( watchedFile , WatchPriority . High ) ;
198+ addToPollingIntervalQueue ( watchedFile , PollingInterval . Low ) ;
189199 }
190- else if ( priority !== WatchPriority . Low ) {
200+ else if ( pollingInterval !== PollingInterval . High ) {
191201 watchedFile . unchangedPolls ++ ;
192202 queue [ pollIndex ] = undefined ;
193- addToPriorityQueue ( watchedFile , priority + 1 ) ;
203+ addToPollingIntervalQueue ( watchedFile , pollingInterval === PollingInterval . Low ? PollingInterval . Medium : PollingInterval . High ) ;
194204 }
195205
196206 if ( queue [ pollIndex ] ) {
@@ -219,24 +229,35 @@ namespace ts {
219229 }
220230 }
221231
222- function addToPriorityQueue ( file : WatchedFile , priority : WatchPriority ) {
223- priorityQueues [ priority ] . push ( file ) ;
224- scheduleNextPollIfNotAlreadyScheduled ( priority ) ;
232+ function pollingIntervalQueue ( pollingInterval : PollingInterval ) {
233+ switch ( pollingInterval ) {
234+ case PollingInterval . Low :
235+ return lowPollingIntervalQueue ;
236+ case PollingInterval . Medium :
237+ return mediumPollingIntervalQueue ;
238+ case PollingInterval . High :
239+ return highPollingIntervalQueue ;
240+ }
241+ }
242+
243+ function addToPollingIntervalQueue ( file : WatchedFile , pollingInterval : PollingInterval ) {
244+ pollingIntervalQueue ( pollingInterval ) . push ( file ) ;
245+ scheduleNextPollIfNotAlreadyScheduled ( pollingInterval ) ;
225246 }
226247
227- function addChangedFileToHighPriorityQueue ( file : WatchedFile ) {
248+ function addChangedFileToLowPollingIntervalQueue ( file : WatchedFile ) {
228249 changedFilesInLastPoll . push ( file ) ;
229- scheduleNextPollIfNotAlreadyScheduled ( WatchPriority . High ) ;
250+ scheduleNextPollIfNotAlreadyScheduled ( PollingInterval . Low ) ;
230251 }
231252
232- function scheduleNextPollIfNotAlreadyScheduled ( priority : WatchPriority ) {
233- if ( ! priorityQueues [ priority ] . pollScheduled ) {
234- scheduleNextPoll ( priority ) ;
253+ function scheduleNextPollIfNotAlreadyScheduled ( pollingInterval : PollingInterval ) {
254+ if ( ! pollingIntervalQueue ( pollingInterval ) . pollScheduled ) {
255+ scheduleNextPoll ( pollingInterval ) ;
235256 }
236257 }
237258
238- function scheduleNextPoll ( priority : WatchPriority ) {
239- priorityQueues [ priority ] . pollScheduled = host . setTimeout ( priority === WatchPriority . High ? pollHighPriorityQueue : pollPriorityQueue , pollingInterval ( priority ) , priorityQueues [ priority ] ) ;
259+ function scheduleNextPoll ( pollingInterval : PollingInterval ) {
260+ pollingIntervalQueue ( pollingInterval ) . pollScheduled = host . setTimeout ( pollingInterval === PollingInterval . Low ? pollLowPollingIntervalQueue : pollPollingIntervalQueue , pollingInterval , pollingIntervalQueue ( pollingInterval ) ) ;
240261 }
241262
242263 function getModifiedTime ( fileName : string ) {
@@ -381,6 +402,7 @@ namespace ts {
381402 }
382403
383404 const useNonPollingWatchers = process . env . TSC_NONPOLLING_WATCHER ;
405+ const tscWatchOption = process . env . TSC_WATCHOPTION ;
384406
385407 const nodeSystem : System = {
386408 args : process . argv . slice ( 2 ) ,
@@ -391,20 +413,6 @@ namespace ts {
391413 } ,
392414 readFile,
393415 writeFile,
394- watchFile : useNonPollingWatchers ? createNonPollingWatchFile ( ) : fsWatchFile ,
395- watchDirectory : ( directoryName , callback , recursive ) => {
396- // Node 4.0 `fs.watch` function supports the "recursive" option on both OSX and Windows
397- // (ref: https://github.com/nodejs/node/pull/2649 and https://github.com/Microsoft/TypeScript/issues/4643)
398- return fsWatchDirectory ( directoryName , ( eventName , relativeFileName ) => {
399- // In watchDirectory we only care about adding and removing files (when event name is
400- // "rename"); changes made within files are handled by corresponding fileWatchers (when
401- // event name is "change")
402- if ( eventName === "rename" ) {
403- // When deleting a file, the passed baseFileName is null
404- callback ( ! relativeFileName ? relativeFileName : normalizePath ( combinePaths ( directoryName , relativeFileName ) ) ) ;
405- }
406- } , recursive ) ;
407- } ,
408416 resolvePath : path => _path . resolve ( path ) ,
409417 fileExists,
410418 directoryExists,
@@ -474,6 +482,20 @@ namespace ts {
474482 process . stdout . write ( "\x1Bc" ) ;
475483 }
476484 } ;
485+ nodeSystem . watchFile = getWatchFile ( ) ;
486+ nodeSystem . watchDirectory = ( directoryName , callback , recursive ) => {
487+ // Node 4.0 `fs.watch` function supports the "recursive" option on both OSX and Windows
488+ // (ref: https://github.com/nodejs/node/pull/2649 and https://github.com/Microsoft/TypeScript/issues/4643)
489+ return fsWatchDirectory ( directoryName , ( eventName , relativeFileName ) => {
490+ // In watchDirectory we only care about adding and removing files (when event name is
491+ // "rename"); changes made within files are handled by corresponding fileWatchers (when
492+ // event name is "change")
493+ if ( eventName === "rename" ) {
494+ // When deleting a file, the passed baseFileName is null
495+ callback ( ! relativeFileName ? relativeFileName : normalizePath ( combinePaths ( directoryName , relativeFileName ) ) ) ;
496+ }
497+ } , recursive ) ;
498+ } ;
477499 return nodeSystem ;
478500
479501 function isFileSystemCaseSensitive ( ) : boolean {
@@ -493,6 +515,20 @@ namespace ts {
493515 } ) ;
494516 }
495517
518+ function getWatchFile ( ) : HostWatchFile {
519+ switch ( tscWatchOption ) {
520+ case "PriorityPollingInterval" :
521+ // Use polling interval based on priority when create watch using host.watchFile
522+ return fsWatchFile ;
523+ case "DynamicPriorityPolling" :
524+ return createDynamicPriorityPollingWatchFile ( nodeSystem ) ;
525+ }
526+ return useNonPollingWatchers ?
527+ createNonPollingWatchFile ( ) :
528+ // Default to do not use polling interval as it is before this experiment branch
529+ ( fileName , callback ) => fsWatchFile ( fileName , callback ) ;
530+ }
531+
496532 function createNonPollingWatchFile ( ) {
497533 // One file can have multiple watchers
498534 const fileWatcherCallbacks = createMultiMap < FileWatcherCallback > ( ) ;
0 commit comments