From fa8d4cba7843b75d68c71fdc86cb490eb50156e8 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 16 Jan 2018 13:15:39 -0800 Subject: [PATCH] Move the polling settings to sys instead of watch utilities --- src/compiler/sys.ts | 182 +++++++++++++--------- src/compiler/watch.ts | 10 +- src/compiler/watchUtilities.ts | 44 ++---- src/harness/unittests/tscWatchMode.ts | 12 +- src/harness/virtualFileSystemWithWatch.ts | 10 +- src/server/editorServices.ts | 8 +- src/server/project.ts | 2 +- 7 files changed, 144 insertions(+), 124 deletions(-) diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 1702911d77e..972918231fe 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -32,10 +32,10 @@ namespace ts { } /* @internal */ - export enum WatchPriority { - High, - Medium, - Low + export enum PollingInterval { + High = 2000, + Medium = 500, + Low = 250 } function getPriorityValues(highPriorityValue: number): [number, number, number] { @@ -44,36 +44,47 @@ namespace ts { return [highPriorityValue, mediumPriorityValue, lowPriorityValue]; } - const pollingIntervalsForPriority = getPriorityValues(250); - function pollingInterval(watchPriority: WatchPriority): number { + function pollingInterval(watchPriority: PollingInterval): number { return pollingIntervalsForPriority[watchPriority]; } + const pollingIntervalsForPriority = getPriorityValues(250); + /* @internal */ - export function watchFileUsingPriorityPollingInterval(host: System, fileName: string, callback: FileWatcherCallback, watchPriority: WatchPriority): FileWatcher { + export function watchFileUsingPriorityPollingInterval(host: System, fileName: string, callback: FileWatcherCallback, watchPriority: PollingInterval): FileWatcher { return host.watchFile(fileName, callback, pollingInterval(watchPriority)); } /* @internal */ - export interface DynamicPriorityPollingStatsSet { - watchFile(fileName: string, callback: FileWatcherCallback, defaultPriority: WatchPriority): FileWatcher; - } + export type HostWatchFile = (fileName: string, callback: FileWatcherCallback, pollingInterval: PollingInterval) => FileWatcher; /* @internal */ export const missingFileModifiedTime = new Date(0); // Any subsequent modification will occur after this time - const chunkSizeOrUnchangedThresholdsForPriority = getPriorityValues(32); - function chunkSize(watchPriority: WatchPriority) { - return chunkSizeOrUnchangedThresholdsForPriority[watchPriority]; + enum ChunkSize { + Low = 32, + Medium = 64, + High = 256 + } + + function chunkSize(pollingInterval: PollingInterval) { + switch (pollingInterval) { + case PollingInterval.Low: + return ChunkSize.Low; + case PollingInterval.Medium: + return ChunkSize.Medium; + case PollingInterval.High: + return ChunkSize.High; + } } /*@internal*/ - export function unChangedThreshold(watchPriority: WatchPriority) { - return chunkSizeOrUnchangedThresholdsForPriority[watchPriority]; + export function unChangedThreshold(pollingInterval: PollingInterval) { + return chunkSize(pollingInterval); } /* @internal */ - export function createDynamicPriorityPollingStatsSet(host: System): DynamicPriorityPollingStatsSet { + export function createDynamicPriorityPollingWatchFile(host: System): HostWatchFile { if (!host.getModifiedTime || !host.setTimeout) { throw notImplemented(); } @@ -83,20 +94,20 @@ namespace ts { unchangedPolls: number; } - interface WatchPriorityQueue extends Array { - watchPriority: WatchPriority; + interface PollingIntervalQueue extends Array { + pollingInterval: PollingInterval; pollIndex: number; pollScheduled: boolean; } const watchedFiles: WatchedFile[] = []; const changedFilesInLastPoll: WatchedFile[] = []; - const priorityQueues = [createPriorityQueue(WatchPriority.High), createPriorityQueue(WatchPriority.Medium), createPriorityQueue(WatchPriority.Low)]; - return { - watchFile - }; + const lowPollingIntervalQueue = createPollingIntervalQueue(PollingInterval.Low); + const mediumPollingIntervalQueue = createPollingIntervalQueue(PollingInterval.Medium); + const highPollingIntervalQueue = createPollingIntervalQueue(PollingInterval.High); + return watchFile; - function watchFile(fileName: string, callback: FileWatcherCallback, defaultPriority: WatchPriority): FileWatcher { + function watchFile(fileName: string, callback: FileWatcherCallback, defaultPollingInterval: PollingInterval): FileWatcher { const file: WatchedFile = { fileName, callback, @@ -105,31 +116,30 @@ namespace ts { }; watchedFiles.push(file); - addToPriorityQueue(file, defaultPriority); + addToPollingIntervalQueue(file, defaultPollingInterval); return { close: () => { file.isClosed = true; // Remove from watchedFiles unorderedRemoveItem(watchedFiles, file); - // Do not update priority queue since that will happen as part of polling + // Do not update polling interval queue since that will happen as part of polling } }; } - function createPriorityQueue(watchPriority: WatchPriority): WatchPriorityQueue { - const queue = [] as WatchPriorityQueue; - queue.watchPriority = watchPriority; + function createPollingIntervalQueue(pollingInterval: PollingInterval): PollingIntervalQueue { + const queue = [] as PollingIntervalQueue; + queue.pollingInterval = pollingInterval; queue.pollIndex = 0; queue.pollScheduled = false; return queue; } - function pollPriorityQueue(queue: WatchPriorityQueue) { - const priority = queue.watchPriority; - queue.pollIndex = pollQueue(queue, priority, queue.pollIndex, chunkSize(priority)); + function pollPollingIntervalQueue(queue: PollingIntervalQueue) { + queue.pollIndex = pollQueue(queue, queue.pollingInterval, queue.pollIndex, chunkSize(queue.pollingInterval)); // Set the next polling index and timeout if (queue.length) { - scheduleNextPoll(priority); + scheduleNextPoll(queue.pollingInterval); } else { Debug.assert(queue.pollIndex === 0); @@ -137,20 +147,20 @@ namespace ts { } } - function pollHighPriorityQueue(queue: WatchPriorityQueue) { + function pollLowPollingIntervalQueue(queue: PollingIntervalQueue) { // Always poll complete list of changedFilesInLastPoll - pollQueue(changedFilesInLastPoll, WatchPriority.High, /*pollIndex*/ 0, changedFilesInLastPoll.length); + pollQueue(changedFilesInLastPoll, PollingInterval.Low, /*pollIndex*/ 0, changedFilesInLastPoll.length); // Finally do the actual polling of the queue - pollPriorityQueue(queue); + pollPollingIntervalQueue(queue); // Schedule poll if there are files in changedFilesInLastPoll but no files in the actual queue - // as pollPriorityQueue wont schedule for next poll + // as pollPollingIntervalQueue wont schedule for next poll if (!queue.pollScheduled && changedFilesInLastPoll.length) { - scheduleNextPoll(WatchPriority.High); + scheduleNextPoll(PollingInterval.Low); } } - function pollQueue(queue: WatchedFile[], priority: WatchPriority, pollIndex: number, chunkSize: number) { + function pollQueue(queue: WatchedFile[], pollingInterval: PollingInterval, pollIndex: number, chunkSize: number) { // Max visit would be all elements of the queue let needsVisit = queue.length; let definedValueCopyToIndex = pollIndex; @@ -175,22 +185,22 @@ namespace ts { // Changed files go to changedFilesInLastPoll queue if (queue !== changedFilesInLastPoll) { queue[pollIndex] = undefined; - addChangedFileToHighPriorityQueue(watchedFile); + addChangedFileToLowPollingIntervalQueue(watchedFile); } } - else if (watchedFile.unchangedPolls !== unChangedThreshold(priority)) { + else if (watchedFile.unchangedPolls !== unChangedThreshold(pollingInterval)) { watchedFile.unchangedPolls++; } else if (queue === changedFilesInLastPoll) { - // Restart unchangedPollCount for unchanged file and move to high priority queue + // Restart unchangedPollCount for unchanged file and move to low polling interval queue watchedFile.unchangedPolls = 1; queue[pollIndex] = undefined; - addToPriorityQueue(watchedFile, WatchPriority.High); + addToPollingIntervalQueue(watchedFile, PollingInterval.Low); } - else if (priority !== WatchPriority.Low) { + else if (pollingInterval !== PollingInterval.High) { watchedFile.unchangedPolls++; queue[pollIndex] = undefined; - addToPriorityQueue(watchedFile, priority + 1); + addToPollingIntervalQueue(watchedFile, pollingInterval === PollingInterval.Low ? PollingInterval.Medium : PollingInterval.High); } if (queue[pollIndex]) { @@ -219,24 +229,35 @@ namespace ts { } } - function addToPriorityQueue(file: WatchedFile, priority: WatchPriority) { - priorityQueues[priority].push(file); - scheduleNextPollIfNotAlreadyScheduled(priority); - } - - function addChangedFileToHighPriorityQueue(file: WatchedFile) { - changedFilesInLastPoll.push(file); - scheduleNextPollIfNotAlreadyScheduled(WatchPriority.High); - } - - function scheduleNextPollIfNotAlreadyScheduled(priority: WatchPriority) { - if (!priorityQueues[priority].pollScheduled) { - scheduleNextPoll(priority); + function pollingIntervalQueue(pollingInterval: PollingInterval) { + switch (pollingInterval) { + case PollingInterval.Low: + return lowPollingIntervalQueue; + case PollingInterval.Medium: + return mediumPollingIntervalQueue; + case PollingInterval.High: + return highPollingIntervalQueue; } } - function scheduleNextPoll(priority: WatchPriority) { - priorityQueues[priority].pollScheduled = host.setTimeout(priority === WatchPriority.High ? pollHighPriorityQueue : pollPriorityQueue, pollingInterval(priority), priorityQueues[priority]); + function addToPollingIntervalQueue(file: WatchedFile, pollingInterval: PollingInterval) { + pollingIntervalQueue(pollingInterval).push(file); + scheduleNextPollIfNotAlreadyScheduled(pollingInterval); + } + + function addChangedFileToLowPollingIntervalQueue(file: WatchedFile) { + changedFilesInLastPoll.push(file); + scheduleNextPollIfNotAlreadyScheduled(PollingInterval.Low); + } + + function scheduleNextPollIfNotAlreadyScheduled(pollingInterval: PollingInterval) { + if (!pollingIntervalQueue(pollingInterval).pollScheduled) { + scheduleNextPoll(pollingInterval); + } + } + + function scheduleNextPoll(pollingInterval: PollingInterval) { + pollingIntervalQueue(pollingInterval).pollScheduled = host.setTimeout(pollingInterval === PollingInterval.Low ? pollLowPollingIntervalQueue : pollPollingIntervalQueue, pollingInterval, pollingIntervalQueue(pollingInterval)); } function getModifiedTime(fileName: string) { @@ -381,6 +402,7 @@ namespace ts { } const useNonPollingWatchers = process.env.TSC_NONPOLLING_WATCHER; + const tscWatchOption = process.env.TSC_WATCHOPTION; const nodeSystem: System = { args: process.argv.slice(2), @@ -391,20 +413,6 @@ namespace ts { }, readFile, writeFile, - watchFile: useNonPollingWatchers ? createNonPollingWatchFile() : fsWatchFile, - watchDirectory: (directoryName, callback, recursive) => { - // Node 4.0 `fs.watch` function supports the "recursive" option on both OSX and Windows - // (ref: https://github.com/nodejs/node/pull/2649 and https://github.com/Microsoft/TypeScript/issues/4643) - return fsWatchDirectory(directoryName, (eventName, relativeFileName) => { - // In watchDirectory we only care about adding and removing files (when event name is - // "rename"); changes made within files are handled by corresponding fileWatchers (when - // event name is "change") - if (eventName === "rename") { - // When deleting a file, the passed baseFileName is null - callback(!relativeFileName ? relativeFileName : normalizePath(combinePaths(directoryName, relativeFileName))); - } - }, recursive); - }, resolvePath: path => _path.resolve(path), fileExists, directoryExists, @@ -474,6 +482,20 @@ namespace ts { process.stdout.write("\x1Bc"); } }; + nodeSystem.watchFile = getWatchFile(); + nodeSystem.watchDirectory = (directoryName, callback, recursive) => { + // Node 4.0 `fs.watch` function supports the "recursive" option on both OSX and Windows + // (ref: https://github.com/nodejs/node/pull/2649 and https://github.com/Microsoft/TypeScript/issues/4643) + return fsWatchDirectory(directoryName, (eventName, relativeFileName) => { + // In watchDirectory we only care about adding and removing files (when event name is + // "rename"); changes made within files are handled by corresponding fileWatchers (when + // event name is "change") + if (eventName === "rename") { + // When deleting a file, the passed baseFileName is null + callback(!relativeFileName ? relativeFileName : normalizePath(combinePaths(directoryName, relativeFileName))); + } + }, recursive); + }; return nodeSystem; function isFileSystemCaseSensitive(): boolean { @@ -493,6 +515,20 @@ namespace ts { }); } + function getWatchFile(): HostWatchFile { + switch (tscWatchOption) { + case "PriorityPollingInterval": + // Use polling interval based on priority when create watch using host.watchFile + return fsWatchFile; + case "DynamicPriorityPolling": + return createDynamicPriorityPollingWatchFile(nodeSystem); + } + return useNonPollingWatchers ? + createNonPollingWatchFile() : + // Default to do not use polling interval as it is before this experiment branch + (fileName, callback) => fsWatchFile(fileName, callback); + } + function createNonPollingWatchFile() { // One file can have multiple watchers const fileWatcherCallbacks = createMultiMap(); diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index d5004ba3920..0d9285921e9 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -257,11 +257,11 @@ namespace ts { const writeLog: (s: string) => void = watchLogLevel !== WatchLogLevel.None ? s => { system.write(s); system.write(system.newLine); } : noop; watchingHost = watchingHost || createWatchingSystemHost(compilerOptions.pretty); const { system, parseConfigFile, reportDiagnostic, reportWatchDiagnostic, beforeCompile, afterCompile } = watchingHost; - const { watchFile, watchFilePath, watchDirectory: watchDirectoryWorker } = getWatchFactory(system, watchLogLevel, writeLog); + const { watchFile, watchFilePath, watchDirectory: watchDirectoryWorker } = getWatchFactory(watchLogLevel, writeLog); const directoryStructureHost = configFileName ? createCachedDirectoryStructureHost(system) : system; if (configFileName) { - watchFile(system, configFileName, scheduleProgramReload, WatchPriority.Low); + watchFile(system, configFileName, scheduleProgramReload, PollingInterval.High); } const getCurrentDirectory = memoize(() => directoryStructureHost.getCurrentDirectory()); @@ -414,7 +414,7 @@ namespace ts { hostSourceFile.sourceFile = sourceFile; sourceFile.version = hostSourceFile.version.toString(); if (!hostSourceFile.fileWatcher) { - hostSourceFile.fileWatcher = watchFilePath(system, fileName, onSourceFileChange, WatchPriority.High, path); + hostSourceFile.fileWatcher = watchFilePath(system, fileName, onSourceFileChange, PollingInterval.Low, path); } } else { @@ -427,7 +427,7 @@ namespace ts { let fileWatcher: FileWatcher; if (sourceFile) { sourceFile.version = "0"; - fileWatcher = watchFilePath(system, fileName, onSourceFileChange, WatchPriority.High, path); + fileWatcher = watchFilePath(system, fileName, onSourceFileChange, PollingInterval.Low, path); sourceFilesCache.set(path, { sourceFile, version: 0, fileWatcher }); } else { @@ -601,7 +601,7 @@ namespace ts { } function watchMissingFilePath(missingFilePath: Path) { - return watchFilePath(system, missingFilePath, onMissingFileChange, WatchPriority.Medium, missingFilePath); + return watchFilePath(system, missingFilePath, onMissingFileChange, PollingInterval.Medium, missingFilePath); } function onMissingFileChange(fileName: string, eventKind: FileWatcherEventKind, missingFilePath: Path) { diff --git a/src/compiler/watchUtilities.ts b/src/compiler/watchUtilities.ts index f35f952834a..75298cc87c0 100644 --- a/src/compiler/watchUtilities.ts +++ b/src/compiler/watchUtilities.ts @@ -96,9 +96,9 @@ namespace ts { Verbose } - export type WatchFile = (host: System, file: string, callback: FileWatcherCallback, watchPriority: WatchPriority, detailInfo1?: X, detailInfo2?: Y) => FileWatcher; + export type WatchFile = (host: System, file: string, callback: FileWatcherCallback, pollingInterval: PollingInterval, detailInfo1?: X, detailInfo2?: Y) => FileWatcher; export type FilePathWatcherCallback = (fileName: string, eventKind: FileWatcherEventKind, filePath: Path) => void; - export type WatchFilePath = (host: System, file: string, callback: FilePathWatcherCallback, watchPriority: WatchPriority, path: Path, detailInfo1?: X, detailInfo2?: Y) => FileWatcher; + export type WatchFilePath = (host: System, file: string, callback: FilePathWatcherCallback, pollingInterval: PollingInterval, path: Path, detailInfo1?: X, detailInfo2?: Y) => FileWatcher; export type WatchDirectory = (host: System, directory: string, callback: DirectoryWatcherCallback, flags: WatchDirectoryFlags, detailInfo1?: X, detailInfo2?: Y) => FileWatcher; export interface WatchFactory { @@ -107,30 +107,15 @@ namespace ts { watchDirectory: WatchDirectory; } - export function getWatchFactory(host: System, watchLogLevel: WatchLogLevel, log: (s: string) => void, getDetailWatchInfo?: GetDetailWatchInfo): WatchFactory { - const value = host.getEnvironmentVariable && host.getEnvironmentVariable("TSC_WATCHFILE"); - switch (value) { - case "PriorityPollingInterval": - // Use polling interval based on priority when create watch using host.watchFile - return getWatchFactoryWith(watchLogLevel, log, getDetailWatchInfo, watchFileUsingPriorityPollingInterval, watchDirectory); - case "DynamicPriorityPolling": - // Dynamically move frequently changing files to high frequency polling and non changing files to lower frequency - return getWatchFactoryWithDynamicPriorityPolling(host, watchLogLevel, log, getDetailWatchInfo); - default: - return getDefaultWatchFactory(watchLogLevel, log, getDetailWatchInfo); - } - } - - export function getDefaultWatchFactory(watchLogLevel: WatchLogLevel, log: (s: string) => void, getDetailWatchInfo?: GetDetailWatchInfo): WatchFactory { - // Current behaviour in which polling interval is always 250 ms + export function getWatchFactory(watchLogLevel: WatchLogLevel, log: (s: string) => void, getDetailWatchInfo?: GetDetailWatchInfo): WatchFactory { return getWatchFactoryWith(watchLogLevel, log, getDetailWatchInfo, watchFile, watchDirectory); } function getWatchFactoryWith(watchLogLevel: WatchLogLevel, log: (s: string) => void, getDetailWatchInfo: GetDetailWatchInfo | undefined, - watchFile: (host: System, file: string, callback: FileWatcherCallback, watchPriority: WatchPriority) => FileWatcher, + watchFile: (host: System, file: string, callback: FileWatcherCallback, watchPriority: PollingInterval) => FileWatcher, watchDirectory: (host: System, directory: string, callback: DirectoryWatcherCallback, flags: WatchDirectoryFlags) => FileWatcher): WatchFactory { - const createFileWatcher: CreateFileWatcher = getCreateFileWatcher(watchLogLevel, watchFile); - const createFilePathWatcher: CreateFileWatcher = watchLogLevel === WatchLogLevel.None ? watchFilePath : createFileWatcher; + const createFileWatcher: CreateFileWatcher = getCreateFileWatcher(watchLogLevel, watchFile); + const createFilePathWatcher: CreateFileWatcher = watchLogLevel === WatchLogLevel.None ? watchFilePath : createFileWatcher; const createDirectoryWatcher: CreateFileWatcher = getCreateFileWatcher(watchLogLevel, watchDirectory); return { watchFile: (host, file, callback, pollingInterval, detailInfo1, detailInfo2) => @@ -141,22 +126,13 @@ namespace ts { createDirectoryWatcher(host, directory, callback, flags, /*passThrough*/ undefined, detailInfo1, detailInfo2, watchDirectory, log, "DirectoryWatcher", getDetailWatchInfo) }; - function watchFilePath(host: System, file: string, callback: FilePathWatcherCallback, watchPriority: WatchPriority, path: Path): FileWatcher { - return watchFile(host, file, (fileName, eventKind) => callback(fileName, eventKind, path), watchPriority); + function watchFilePath(host: System, file: string, callback: FilePathWatcherCallback, pollingInterval: PollingInterval, path: Path): FileWatcher { + return watchFile(host, file, (fileName, eventKind) => callback(fileName, eventKind, path), pollingInterval); } } - function getWatchFactoryWithDynamicPriorityPolling(host: System, watchLogLevel: WatchLogLevel, log: (s: string) => void, getDetailWatchInfo?: GetDetailWatchInfo): WatchFactory { - const pollingSet = createDynamicPriorityPollingStatsSet(host); - return getWatchFactoryWith(watchLogLevel, log, getDetailWatchInfo, watchFile, watchDirectory); - - function watchFile(_host: System, file: string, callback: FileWatcherCallback, watchPriority: WatchPriority): FileWatcher { - return pollingSet.watchFile(file, callback, watchPriority); - } - } - - function watchFile(host: System, file: string, callback: FileWatcherCallback, _watchPriority: WatchPriority): FileWatcher { - return host.watchFile(file, callback); + function watchFile(host: System, file: string, callback: FileWatcherCallback, pollingInterval: PollingInterval): FileWatcher { + return host.watchFile(file, callback, pollingInterval); } function watchDirectory(host: System, directory: string, callback: DirectoryWatcherCallback, flags: WatchDirectoryFlags): FileWatcher { diff --git a/src/harness/unittests/tscWatchMode.ts b/src/harness/unittests/tscWatchMode.ts index 60361f56c1a..ffdf02c4eb1 100644 --- a/src/harness/unittests/tscWatchMode.ts +++ b/src/harness/unittests/tscWatchMode.ts @@ -2123,15 +2123,15 @@ declare module "fs" { }; const files = [file1, libFile]; const environmentVariables = createMap(); - environmentVariables.set("TSC_WATCHFILE", "DynamicPriorityPolling"); + environmentVariables.set("TSC_WATCHOPTION", "DynamicPriorityPolling"); const host = createWatchedSystem(files, { environmentVariables }); const watch = createWatchModeWithoutConfigFile([file1.path], host); const initialProgram = watch(); verifyProgram(); - const mediumPriorityThreshold = unChangedThreshold(WatchPriority.Medium); - for (let index = 0; index < mediumPriorityThreshold; index++) { + const mediumPollingIntervalThreshold = unChangedThreshold(PollingInterval.Medium); + for (let index = 0; index < mediumPollingIntervalThreshold; index++) { // Transition libFile and file1 to low priority queue host.checkTimeoutQueueLengthAndRun(1); assert.deepEqual(watch(), initialProgram); @@ -2157,14 +2157,14 @@ declare module "fs" { assert.isTrue(host.fileExists(outputFile1)); assert.equal(host.readFile(outputFile1), file1.content + host.newLine); - const newThreshold = unChangedThreshold(WatchPriority.High) + mediumPriorityThreshold; + const newThreshold = unChangedThreshold(PollingInterval.Low) + mediumPollingIntervalThreshold; for (; fileUnchangeDetected < newThreshold; fileUnchangeDetected++) { - // For low + Medium/high priority + // For high + Medium/low polling interval host.checkTimeoutQueueLengthAndRun(2); assert.deepEqual(watch(), newProgram); } - // Everything goes in low priority queue + // Everything goes in high polling interval queue host.checkTimeoutQueueLengthAndRun(1); assert.deepEqual(watch(), newProgram); diff --git a/src/harness/virtualFileSystemWithWatch.ts b/src/harness/virtualFileSystemWithWatch.ts index 6fe5bac459f..7f732a9c672 100644 --- a/src/harness/virtualFileSystemWithWatch.ts +++ b/src/harness/virtualFileSystemWithWatch.ts @@ -262,6 +262,7 @@ interface Array {}` readonly watchedFiles = createMultiMap(); private readonly executingFilePath: string; private readonly currentDirectory: string; + private readonly dynamicPriorityWatchFile: HostWatchFile; constructor(public withSafeList: boolean, public useCaseSensitiveFileNames: boolean, executingFilePath: string, currentDirectory: string, fileOrFolderList: ReadonlyArray, public readonly newLine = "\n", public readonly useWindowsStylePath?: boolean, private readonly environmentVariables?: Map) { this.getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames); @@ -269,6 +270,9 @@ interface Array {}` this.executingFilePath = this.getHostSpecificPath(executingFilePath); this.currentDirectory = this.getHostSpecificPath(currentDirectory); this.reloadFS(fileOrFolderList); + this.dynamicPriorityWatchFile = this.environmentVariables && this.environmentVariables.get("TSC_WATCHOPTION") === "DynamicPriorityPolling" ? + createDynamicPriorityPollingWatchFile(this) : + undefined; } getNewLine() { @@ -602,7 +606,11 @@ interface Array {}` return Harness.mockHash(s); } - watchFile(fileName: string, cb: FileWatcherCallback) { + watchFile(fileName: string, cb: FileWatcherCallback, pollingInterval: number) { + if (this.dynamicPriorityWatchFile) { + return this.dynamicPriorityWatchFile(fileName, cb, pollingInterval); + } + const path = this.toFullPath(fileName); const callback: TestFileWatcher = { fileName, cb }; this.watchedFiles.add(path, callback); diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index e6153d91069..929abbd001f 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -449,7 +449,7 @@ namespace ts.server { const watchLogLevel = this.logger.hasLevel(LogLevel.verbose) ? WatchLogLevel.Verbose : this.logger.loggingEnabled() ? WatchLogLevel.TriggerOnly : WatchLogLevel.None; const log: (s: string) => void = watchLogLevel !== WatchLogLevel.None ? (s => this.logger.info(s)) : noop; - this.watchFactory = getDefaultWatchFactory(watchLogLevel, log, getDetailWatchInfo); + this.watchFactory = getWatchFactory(watchLogLevel, log, getDetailWatchInfo); } toPath(fileName: string) { @@ -1116,7 +1116,7 @@ namespace ts.server { this.host, configFileName, (_filename, eventKind) => this.onConfigFileChangeForOpenScriptInfo(configFileName, eventKind), - WatchPriority.Low, + PollingInterval.High, WatchType.ConfigFileForInferredRoot ); this.logConfigFileWatchUpdate(configFileName, canonicalConfigFilePath, configFileExistenceInfo, ConfigFileWatcherStatus.UpdatedCallback); @@ -1500,7 +1500,7 @@ namespace ts.server { this.host, configFileName, (_fileName, eventKind) => this.onConfigChangedForConfiguredProject(project, eventKind), - WatchPriority.Low, + PollingInterval.High, WatchType.ConfigFilePath, project ); @@ -1727,7 +1727,7 @@ namespace ts.server { this.host, fileName, (fileName, eventKind, path) => this.onSourceFileChanged(fileName, eventKind, path), - WatchPriority.Medium, + PollingInterval.Medium, info.path, WatchType.ClosedScriptInfo ); diff --git a/src/server/project.ts b/src/server/project.ts index c7ea875f072..f1c4dd6af85 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -932,7 +932,7 @@ namespace ts.server { this.projectService.delayUpdateProjectGraphAndInferredProjectsRefresh(this); } }, - WatchPriority.Medium, + PollingInterval.Medium, WatchType.MissingFilePath, this );