Skip to content

Commit aa5e49a

Browse files
committed
Update watchFile to take priority (which is currently polling interval being passed to the host)
1 parent cabb211 commit aa5e49a

5 files changed

Lines changed: 76 additions & 48 deletions

File tree

src/compiler/sys.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,23 @@ namespace ts {
3030
mtime?: Date;
3131
}
3232

33+
/* @internal */
34+
export enum WatchPriority {
35+
High,
36+
Medium,
37+
Low
38+
}
39+
40+
const pollingIntervalsForPriority = [250, 1000, 4000];
41+
function pollingInterval(watchPriority: WatchPriority): number {
42+
return pollingIntervalsForPriority[watchPriority];
43+
}
44+
45+
/* @internal */
46+
export function watchFileUsingPriorityPollingInterval(host: System, fileName: string, callback: FileWatcherCallback, watchPriority: WatchPriority): FileWatcher {
47+
return host.watchFile(fileName, callback, pollingInterval(watchPriority));
48+
}
49+
3350
/**
3451
* Partial interface of the System thats needed to support the caching of directory structure
3552
*/

src/compiler/watch.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -255,16 +255,13 @@ namespace ts {
255255
const watchLogLevel = compilerOptions.extendedDiagnostics ? WatchLogLevel.Verbose :
256256
compilerOptions.diagnostics ? WatchLogLevel.TriggerOnly : WatchLogLevel.None;
257257
const writeLog: (s: string) => void = watchLogLevel !== WatchLogLevel.None ? s => { system.write(s); system.write(system.newLine); } : noop;
258-
const watchFile = createWatchFile(watchLogLevel, writeLog);
259-
const watchFilePath = createWatchFilePath(watchLogLevel, writeLog);
260-
const watchDirectoryWorker = createWatchDirectory(watchLogLevel, writeLog);
261-
262258
watchingHost = watchingHost || createWatchingSystemHost(compilerOptions.pretty);
263259
const { system, parseConfigFile, reportDiagnostic, reportWatchDiagnostic, beforeCompile, afterCompile } = watchingHost;
260+
const { watchFile, watchFilePath, watchDirectory: watchDirectoryWorker } = getWatchFactory(system, watchLogLevel, writeLog);
264261

265262
const directoryStructureHost = configFileName ? createCachedDirectoryStructureHost(system) : system;
266263
if (configFileName) {
267-
watchFile(system, configFileName, scheduleProgramReload, /*pollingInterval*/ undefined);
264+
watchFile(system, configFileName, scheduleProgramReload, WatchPriority.Low);
268265
}
269266

270267
const getCurrentDirectory = memoize(() => directoryStructureHost.getCurrentDirectory());
@@ -417,7 +414,7 @@ namespace ts {
417414
hostSourceFile.sourceFile = sourceFile;
418415
sourceFile.version = hostSourceFile.version.toString();
419416
if (!hostSourceFile.fileWatcher) {
420-
hostSourceFile.fileWatcher = watchFilePath(system, fileName, onSourceFileChange, /*pollingInterval*/ undefined, path);
417+
hostSourceFile.fileWatcher = watchFilePath(system, fileName, onSourceFileChange, WatchPriority.High, path);
421418
}
422419
}
423420
else {
@@ -430,7 +427,7 @@ namespace ts {
430427
let fileWatcher: FileWatcher;
431428
if (sourceFile) {
432429
sourceFile.version = "0";
433-
fileWatcher = watchFilePath(system, fileName, onSourceFileChange, /*pollingInterval*/ undefined, path);
430+
fileWatcher = watchFilePath(system, fileName, onSourceFileChange, WatchPriority.High, path);
434431
sourceFilesCache.set(path, { sourceFile, version: 0, fileWatcher });
435432
}
436433
else {
@@ -604,7 +601,7 @@ namespace ts {
604601
}
605602

606603
function watchMissingFilePath(missingFilePath: Path) {
607-
return watchFilePath(system, missingFilePath, onMissingFileChange, /*pollingInterval*/ undefined, missingFilePath);
604+
return watchFilePath(system, missingFilePath, onMissingFileChange, WatchPriority.Medium, missingFilePath);
608605
}
609606

610607
function onMissingFileChange(fileName: string, eventKind: FileWatcherEventKind, missingFilePath: Path) {

src/compiler/watchUtilities.ts

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -96,43 +96,63 @@ namespace ts {
9696
Verbose
9797
}
9898

99-
export type WatchFile<X, Y> = (host: System, file: string, callback: FileWatcherCallback, pollingInterval?: number, detailInfo1?: X, detailInfo2?: Y) => FileWatcher;
99+
export type WatchFile<X, Y> = (host: System, file: string, callback: FileWatcherCallback, watchPriority: WatchPriority, detailInfo1?: X, detailInfo2?: Y) => FileWatcher;
100100
export type FilePathWatcherCallback = (fileName: string, eventKind: FileWatcherEventKind, filePath: Path) => void;
101-
export type WatchFilePath<X, Y> = (host: System, file: string, callback: FilePathWatcherCallback, pollingInterval: number | undefined, path: Path, detailInfo1?: X, detailInfo2?: Y) => FileWatcher;
101+
export type WatchFilePath<X, Y> = (host: System, file: string, callback: FilePathWatcherCallback, watchPriority: WatchPriority, path: Path, detailInfo1?: X, detailInfo2?: Y) => FileWatcher;
102102
export type WatchDirectory<X, Y> = (host: System, directory: string, callback: DirectoryWatcherCallback, flags: WatchDirectoryFlags, detailInfo1?: X, detailInfo2?: Y) => FileWatcher;
103103

104-
export function createWatchFile<X = undefined, Y = undefined>(watchLogLevel: WatchLogLevel, log: (s: string) => void, getDetailWatchInfo?: GetDetailWatchInfo<X, Y>): WatchFile<X, Y> {
105-
const createFileWatcher: CreateFileWatcher<number | undefined, FileWatcherEventKind, undefined, X, Y> = getCreateFileWatcher(watchLogLevel, watchFile);
106-
return (host, file, callback, pollingInterval, detailInfo1, detailInfo2) =>
107-
createFileWatcher(host, file, callback, pollingInterval, /*passThrough*/ undefined, detailInfo1, detailInfo2, watchFile, log, "FileWatcher", getDetailWatchInfo);
104+
export interface WatchFactory<X, Y> {
105+
watchFile: WatchFile<X, Y>;
106+
watchFilePath: WatchFilePath<X, Y>;
107+
watchDirectory: WatchDirectory<X, Y>;
108108
}
109109

110-
export function createWatchFilePath<X = undefined, Y = undefined>(watchLogLevel: WatchLogLevel, log: (s: string) => void, getDetailWatchInfo?: GetDetailWatchInfo<X, Y>): WatchFilePath<X, Y> {
111-
const createFileWatcher: CreateFileWatcher<number | undefined, FileWatcherEventKind, Path, X, Y> = getCreateFileWatcher(watchLogLevel, watchFilePath);
112-
return (host, file, callback, pollingInterval, path, detailInfo1, detailInfo2) =>
113-
createFileWatcher(host, file, callback, pollingInterval, path, detailInfo1, detailInfo2, watchFile, log, "FileWatcher", getDetailWatchInfo);
110+
export function getWatchFactory<X = undefined, Y = undefined>(host: System, watchLogLevel: WatchLogLevel, log: (s: string) => void, getDetailWatchInfo?: GetDetailWatchInfo<X, Y>): WatchFactory<X, Y> {
111+
const value = host.getEnvironmentVariable("TSC_WATCHFILE");
112+
switch (value) {
113+
case "PriorityPollingInterval":
114+
// Use polling interval based on priority when create watch using host.watchFile
115+
return getWatchFactoryWith(watchLogLevel, log, getDetailWatchInfo, watchFileUsingPriorityPollingInterval, watchDirectory);
116+
default:
117+
return getDefaultWatchFactory(watchLogLevel, log, getDetailWatchInfo);
118+
}
114119
}
115120

116-
export function createWatchDirectory<X = undefined, Y = undefined>(watchLogLevel: WatchLogLevel, log: (s: string) => void, getDetailWatchInfo?: GetDetailWatchInfo<X, Y>): WatchDirectory<X, Y> {
117-
const createFileWatcher: CreateFileWatcher<WatchDirectoryFlags, undefined, undefined, X, Y> = getCreateFileWatcher(watchLogLevel, watchDirectory);
118-
return (host, directory, callback, flags, detailInfo1, detailInfo2) =>
119-
createFileWatcher(host, directory, callback, flags, /*passThrough*/ undefined, detailInfo1, detailInfo2, watchDirectory, log, "DirectoryWatcher", getDetailWatchInfo);
121+
export function getDefaultWatchFactory<X = undefined, Y = undefined>(watchLogLevel: WatchLogLevel, log: (s: string) => void, getDetailWatchInfo?: GetDetailWatchInfo<X, Y>): WatchFactory<X, Y> {
122+
// Current behaviour in which polling interval is always 250 ms
123+
return getWatchFactoryWith(watchLogLevel, log, getDetailWatchInfo, watchFile, watchDirectory);
120124
}
121125

122-
function watchFile(host: System, file: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher {
123-
return host.watchFile(file, callback, pollingInterval);
126+
function getWatchFactoryWith<X = undefined, Y = undefined>(watchLogLevel: WatchLogLevel, log: (s: string) => void, getDetailWatchInfo: GetDetailWatchInfo<X, Y> | undefined,
127+
watchFile: (host: System, file: string, callback: FileWatcherCallback, watchPriority: WatchPriority) => FileWatcher,
128+
watchDirectory: (host: System, directory: string, callback: DirectoryWatcherCallback, flags: WatchDirectoryFlags) => FileWatcher): WatchFactory<X, Y> {
129+
const createFileWatcher: CreateFileWatcher<WatchPriority, FileWatcherEventKind, undefined, X, Y> = getCreateFileWatcher(watchLogLevel, watchFile);
130+
const createFilePathWatcher: CreateFileWatcher<WatchPriority, FileWatcherEventKind, Path, X, Y> = watchLogLevel === WatchLogLevel.None ? watchFilePath : createFileWatcher;
131+
const createDirectoryWatcher: CreateFileWatcher<WatchDirectoryFlags, undefined, undefined, X, Y> = getCreateFileWatcher(watchLogLevel, watchDirectory);
132+
return {
133+
watchFile: (host, file, callback, pollingInterval, detailInfo1, detailInfo2) =>
134+
createFileWatcher(host, file, callback, pollingInterval, /*passThrough*/ undefined, detailInfo1, detailInfo2, watchFile, log, "FileWatcher", getDetailWatchInfo),
135+
watchFilePath: (host, file, callback, pollingInterval, path, detailInfo1, detailInfo2) =>
136+
createFilePathWatcher(host, file, callback, pollingInterval, path, detailInfo1, detailInfo2, watchFile, log, "FileWatcher", getDetailWatchInfo),
137+
watchDirectory: (host, directory, callback, flags, detailInfo1, detailInfo2) =>
138+
createDirectoryWatcher(host, directory, callback, flags, /*passThrough*/ undefined, detailInfo1, detailInfo2, watchDirectory, log, "DirectoryWatcher", getDetailWatchInfo)
139+
};
140+
141+
function watchFilePath(host: System, file: string, callback: FilePathWatcherCallback, watchPriority: WatchPriority, path: Path): FileWatcher {
142+
return watchFile(host, file, (fileName, eventKind) => callback(fileName, eventKind, path), watchPriority);
143+
}
124144
}
125145

126-
function watchFilePath(host: System, file: string, callback: FilePathWatcherCallback, pollingInterval: number | undefined, path: Path): FileWatcher {
127-
return host.watchFile(file, (fileName, eventKind) => callback(fileName, eventKind, path), pollingInterval);
146+
function watchFile(host: System, file: string, callback: FileWatcherCallback, _watchPriority: WatchPriority): FileWatcher {
147+
return host.watchFile(file, callback);
128148
}
129149

130150
function watchDirectory(host: System, directory: string, callback: DirectoryWatcherCallback, flags: WatchDirectoryFlags): FileWatcher {
131151
return host.watchDirectory(directory, callback, (flags & WatchDirectoryFlags.Recursive) !== 0);
132152
}
133153

134-
export type WatchCallback<T, U> = (fileName: string, cbOptional?: T, passThrough?: U) => void;
135-
export type AddWatch<T, U, V> = (host: System, file: string, cb: WatchCallback<U, V>, flags: T, passThrough?: V, detailInfo1?: undefined, detailInfo2?: undefined) => FileWatcher;
154+
type WatchCallback<T, U> = (fileName: string, cbOptional?: T, passThrough?: U) => void;
155+
type AddWatch<T, U, V> = (host: System, file: string, cb: WatchCallback<U, V>, flags: T, passThrough?: V, detailInfo1?: undefined, detailInfo2?: undefined) => FileWatcher;
136156
export type GetDetailWatchInfo<X, Y> = (detailInfo1: X, detailInfo2: Y) => string;
137157

138158
type CreateFileWatcher<T, U, V, X, Y> = (host: System, file: string, cb: WatchCallback<U, V>, flags: T, passThrough: V | undefined, detailInfo1: X | undefined, detailInfo2: Y | undefined, addWatch: AddWatch<T, U, V>, log: (s: string) => void, watchCaption: string, getDetailWatchInfo: GetDetailWatchInfo<X, Y> | undefined) => FileWatcher;
@@ -170,7 +190,7 @@ namespace ts {
170190
}
171191

172192
function getWatchInfo<T, X, Y>(file: string, flags: T, detailInfo1: X | undefined, detailInfo2: Y | undefined, getDetailWatchInfo: GetDetailWatchInfo<X, Y> | undefined) {
173-
return `WatchInfo: ${file} ${flags} ${getDetailWatchInfo ? getDetailWatchInfo(detailInfo1, detailInfo2) : ""}`
193+
return `WatchInfo: ${file} ${flags} ${getDetailWatchInfo ? getDetailWatchInfo(detailInfo1, detailInfo2) : ""}`;
174194
}
175195

176196
export function closeFileWatcher(watcher: FileWatcher) {

src/server/editorServices.ts

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -410,11 +410,7 @@ namespace ts.server {
410410
private readonly seenProjects = createMap<true>();
411411

412412
/*@internal*/
413-
readonly watchFile: WatchFile;
414-
/*@internal*/
415-
readonly watchFilePath: WatchFilePath;
416-
/*@internal*/
417-
readonly watchDirectory: WatchDirectory;
413+
readonly watchFactory: WatchFactory<WatchType, Project>;
418414

419415
constructor(opts: ProjectServiceOptions) {
420416
this.host = opts.host;
@@ -457,9 +453,7 @@ namespace ts.server {
457453
const watchLogLevel = this.logger.hasLevel(LogLevel.verbose) ? WatchLogLevel.Verbose :
458454
this.logger.loggingEnabled() ? WatchLogLevel.TriggerOnly : WatchLogLevel.None;
459455
const log: (s: string) => void = watchLogLevel !== WatchLogLevel.None ? (s => this.logger.info(s)) : noop;
460-
this.watchFile = createWatchFile(watchLogLevel, log, getDetailWatchInfo);
461-
this.watchFilePath = createWatchFilePath(watchLogLevel, log, getDetailWatchInfo);
462-
this.watchDirectory = createWatchDirectory(watchLogLevel, log, getDetailWatchInfo);
456+
this.watchFactory = getDefaultWatchFactory(watchLogLevel, log, getDetailWatchInfo);
463457
}
464458

465459
toPath(fileName: string) {
@@ -792,7 +786,7 @@ namespace ts.server {
792786
*/
793787
/*@internal*/
794788
watchWildcardDirectory(directory: Path, flags: WatchDirectoryFlags, project: ConfiguredProject) {
795-
return this.watchDirectory(
789+
return this.watchFactory.watchDirectory(
796790
this.host,
797791
directory,
798792
fileOrDirectory => {
@@ -1122,11 +1116,11 @@ namespace ts.server {
11221116
canonicalConfigFilePath: string,
11231117
configFileExistenceInfo: ConfigFileExistenceInfo
11241118
) {
1125-
configFileExistenceInfo.configFileWatcherForRootOfInferredProject = this.watchFile(
1119+
configFileExistenceInfo.configFileWatcherForRootOfInferredProject = this.watchFactory.watchFile(
11261120
this.host,
11271121
configFileName,
11281122
(_filename, eventKind) => this.onConfigFileChangeForOpenScriptInfo(configFileName, eventKind),
1129-
/*pollingInterval*/ undefined,
1123+
WatchPriority.Low,
11301124
WatchType.ConfigFileForInferredRoot
11311125
);
11321126
this.logConfigFileWatchUpdate(configFileName, canonicalConfigFilePath, configFileExistenceInfo, ConfigFileWatcherStatus.UpdatedCallback);
@@ -1506,11 +1500,11 @@ namespace ts.server {
15061500

15071501
project.configFileSpecs = configFileSpecs;
15081502
// TODO: We probably should also watch the configFiles that are extended
1509-
project.configFileWatcher = this.watchFile(
1503+
project.configFileWatcher = this.watchFactory.watchFile(
15101504
this.host,
15111505
configFileName,
15121506
(_fileName, eventKind) => this.onConfigChangedForConfiguredProject(project, eventKind),
1513-
/*pollingInterval*/ undefined,
1507+
WatchPriority.Low,
15141508
WatchType.ConfigFilePath,
15151509
project
15161510
);
@@ -1733,11 +1727,11 @@ namespace ts.server {
17331727
// do not watch files with mixed content - server doesn't know how to interpret it
17341728
if (!info.isDynamicOrHasMixedContent()) {
17351729
const { fileName } = info;
1736-
info.fileWatcher = this.watchFilePath(
1730+
info.fileWatcher = this.watchFactory.watchFilePath(
17371731
this.host,
17381732
fileName,
17391733
(fileName, eventKind, path) => this.onSourceFileChanged(fileName, eventKind, path),
1740-
/*pollingInterval*/ undefined,
1734+
WatchPriority.Medium,
17411735
info.path,
17421736
WatchType.ClosedScriptInfo
17431737
);

src/server/project.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ namespace ts.server {
376376

377377
/*@internal*/
378378
watchDirectoryOfFailedLookupLocation(directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags) {
379-
return this.projectService.watchDirectory(
379+
return this.projectService.watchFactory.watchDirectory(
380380
this.projectService.host,
381381
directory,
382382
cb,
@@ -393,7 +393,7 @@ namespace ts.server {
393393

394394
/*@internal*/
395395
watchTypeRootsDirectory(directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags) {
396-
return this.projectService.watchDirectory(
396+
return this.projectService.watchFactory.watchDirectory(
397397
this.projectService.host,
398398
directory,
399399
cb,
@@ -916,7 +916,7 @@ namespace ts.server {
916916
}
917917

918918
private addMissingFileWatcher(missingFilePath: Path) {
919-
const fileWatcher = this.projectService.watchFile(
919+
const fileWatcher = this.projectService.watchFactory.watchFile(
920920
this.projectService.host,
921921
missingFilePath,
922922
(fileName, eventKind) => {
@@ -932,7 +932,7 @@ namespace ts.server {
932932
this.projectService.delayUpdateProjectGraphAndInferredProjectsRefresh(this);
933933
}
934934
},
935-
/*pollingInterval*/ undefined,
935+
WatchPriority.Medium,
936936
WatchType.MissingFilePath,
937937
this
938938
);

0 commit comments

Comments
 (0)