diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index 79e26f5986c..4913e54f043 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -252,18 +252,19 @@ namespace ts { let hasChangedCompilerOptions = false; // True if the compiler options have changed between compilations let hasChangedAutomaticTypeDirectiveNames = false; // True if the automatic type directives have changed - const loggingEnabled = compilerOptions.diagnostics || compilerOptions.extendedDiagnostics; - const writeLog: (s: string) => void = loggingEnabled ? s => { system.write(s); system.write(system.newLine); } : noop; - const watchFile = compilerOptions.extendedDiagnostics ? ts.addFileWatcherWithLogging : loggingEnabled ? ts.addFileWatcherWithOnlyTriggerLogging : ts.addFileWatcher; - const watchFilePath = compilerOptions.extendedDiagnostics ? ts.addFilePathWatcherWithLogging : ts.addFilePathWatcher; - const watchDirectoryWorker = compilerOptions.extendedDiagnostics ? ts.addDirectoryWatcherWithLogging : ts.addDirectoryWatcher; + const watchLogLevel = compilerOptions.extendedDiagnostics ? WatchLogLevel.Verbose : + compilerOptions.diagnostics ? WatchLogLevel.TriggerOnly : WatchLogLevel.None; + const writeLog: (s: string) => void = watchLogLevel !== WatchLogLevel.None ? s => { system.write(s); system.write(system.newLine); } : noop; + const watchFile = createWatchFile(watchLogLevel, writeLog); + const watchFilePath = createWatchFilePath(watchLogLevel, writeLog); + const watchDirectoryWorker = createWatchDirectory(watchLogLevel, writeLog); watchingHost = watchingHost || createWatchingSystemHost(compilerOptions.pretty); const { system, parseConfigFile, reportDiagnostic, reportWatchDiagnostic, beforeCompile, afterCompile } = watchingHost; const directoryStructureHost = configFileName ? createCachedDirectoryStructureHost(system) : system; if (configFileName) { - watchFile(system, configFileName, scheduleProgramReload, writeLog); + watchFile(system, configFileName, scheduleProgramReload, /*pollingInterval*/ undefined); } const getCurrentDirectory = memoize(() => directoryStructureHost.getCurrentDirectory()); @@ -416,7 +417,7 @@ namespace ts { hostSourceFile.sourceFile = sourceFile; sourceFile.version = hostSourceFile.version.toString(); if (!hostSourceFile.fileWatcher) { - hostSourceFile.fileWatcher = watchFilePath(system, fileName, onSourceFileChange, path, writeLog); + hostSourceFile.fileWatcher = watchFilePath(system, fileName, onSourceFileChange, /*pollingInterval*/ undefined, path); } } else { @@ -429,7 +430,7 @@ namespace ts { let fileWatcher: FileWatcher; if (sourceFile) { sourceFile.version = "0"; - fileWatcher = watchFilePath(system, fileName, onSourceFileChange, path, writeLog); + fileWatcher = watchFilePath(system, fileName, onSourceFileChange, /*pollingInterval*/ undefined, path); sourceFilesCache.set(path, { sourceFile, version: 0, fileWatcher }); } else { @@ -599,11 +600,11 @@ namespace ts { } function watchDirectory(directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags) { - return watchDirectoryWorker(system, directory, cb, flags, writeLog); + return watchDirectoryWorker(system, directory, cb, flags); } function watchMissingFilePath(missingFilePath: Path) { - return watchFilePath(system, missingFilePath, onMissingFileChange, missingFilePath, writeLog); + return watchFilePath(system, missingFilePath, onMissingFileChange, /*pollingInterval*/ undefined, missingFilePath); } function onMissingFileChange(fileName: string, eventKind: FileWatcherEventKind, missingFilePath: Path) { diff --git a/src/compiler/watchUtilities.ts b/src/compiler/watchUtilities.ts index 0bc6920479b..70d7ca63ee9 100644 --- a/src/compiler/watchUtilities.ts +++ b/src/compiler/watchUtilities.ts @@ -90,75 +90,89 @@ namespace ts { return program.isEmittedFile(file); } - export function addFileWatcher(host: System, file: string, cb: FileWatcherCallback): FileWatcher { - return host.watchFile(file, cb); - } - - export function addFileWatcherWithLogging(host: System, file: string, cb: FileWatcherCallback, log: (s: string) => void): FileWatcher { - const watcherCaption = `FileWatcher:: `; - return createWatcherWithLogging(addFileWatcher, watcherCaption, log, /*logOnlyTrigger*/ false, host, file, cb); - } - - export function addFileWatcherWithOnlyTriggerLogging(host: System, file: string, cb: FileWatcherCallback, log: (s: string) => void): FileWatcher { - const watcherCaption = `FileWatcher:: `; - return createWatcherWithLogging(addFileWatcher, watcherCaption, log, /*logOnlyTrigger*/ true, host, file, cb); + export enum WatchLogLevel { + None, + TriggerOnly, + Verbose } + export type WatchFile = (host: System, file: string, callback: FileWatcherCallback, pollingInterval?: number, detailInfo1?: X, detailInfo2?: Y) => FileWatcher; export type FilePathWatcherCallback = (fileName: string, eventKind: FileWatcherEventKind, filePath: Path) => void; - export function addFilePathWatcher(host: System, file: string, cb: FilePathWatcherCallback, path: Path): FileWatcher { - return host.watchFile(file, (fileName, eventKind) => cb(fileName, eventKind, path)); + export type WatchFilePath = (host: System, file: string, callback: FilePathWatcherCallback, pollingInterval: number | undefined, path: Path, detailInfo1?: X, detailInfo2?: Y) => FileWatcher; + export type WatchDirectory = (host: System, directory: string, callback: DirectoryWatcherCallback, flags: WatchDirectoryFlags, detailInfo1?: X, detailInfo2?: Y) => FileWatcher; + + export function createWatchFile(watchLogLevel: WatchLogLevel, log: (s: string) => void, getDetailWatchInfo?: GetDetailWatchInfo): WatchFile { + const createFileWatcher: CreateFileWatcher = getCreateFileWatcher(watchLogLevel, watchFile); + return (host, file, callback, pollingInterval, detailInfo1, detailInfo2) => + createFileWatcher(host, file, callback, pollingInterval, /*passThrough*/ undefined, detailInfo1, detailInfo2, watchFile, log, "FileWatcher", getDetailWatchInfo); } - export function addFilePathWatcherWithLogging(host: System, file: string, cb: FilePathWatcherCallback, path: Path, log: (s: string) => void): FileWatcher { - const watcherCaption = `FileWatcher:: `; - return createWatcherWithLogging(addFileWatcher, watcherCaption, log, /*logOnlyTrigger*/ false, host, file, cb, path); + export function createWatchFilePath(watchLogLevel: WatchLogLevel, log: (s: string) => void, getDetailWatchInfo?: GetDetailWatchInfo): WatchFilePath { + const createFileWatcher: CreateFileWatcher = getCreateFileWatcher(watchLogLevel, watchFilePath); + return (host, file, callback, pollingInterval, path, detailInfo1, detailInfo2) => + createFileWatcher(host, file, callback, pollingInterval, path, detailInfo1, detailInfo2, watchFile, log, "FileWatcher", getDetailWatchInfo); } - export function addFilePathWatcherWithOnlyTriggerLogging(host: System, file: string, cb: FilePathWatcherCallback, path: Path, log: (s: string) => void): FileWatcher { - const watcherCaption = `FileWatcher:: `; - return createWatcherWithLogging(addFileWatcher, watcherCaption, log, /*logOnlyTrigger*/ true, host, file, cb, path); + export function createWatchDirectory(watchLogLevel: WatchLogLevel, log: (s: string) => void, getDetailWatchInfo?: GetDetailWatchInfo): WatchDirectory { + const createFileWatcher: CreateFileWatcher = getCreateFileWatcher(watchLogLevel, watchDirectory); + return (host, directory, callback, flags, detailInfo1, detailInfo2) => + createFileWatcher(host, directory, callback, flags, /*passThrough*/ undefined, detailInfo1, detailInfo2, watchDirectory, log, "DirectoryWatcher", getDetailWatchInfo); } - export function addDirectoryWatcher(host: System, directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags): FileWatcher { - const recursive = (flags & WatchDirectoryFlags.Recursive) !== 0; - return host.watchDirectory(directory, cb, recursive); + function watchFile(host: System, file: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher { + return host.watchFile(file, callback, pollingInterval); } - export function addDirectoryWatcherWithLogging(host: System, directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags, log: (s: string) => void): FileWatcher { - const watcherCaption = `DirectoryWatcher ${(flags & WatchDirectoryFlags.Recursive) !== 0 ? "recursive" : ""}:: `; - return createWatcherWithLogging(addDirectoryWatcher, watcherCaption, log, /*logOnlyTrigger*/ false, host, directory, cb, flags); + function watchFilePath(host: System, file: string, callback: FilePathWatcherCallback, pollingInterval: number | undefined, path: Path): FileWatcher { + return host.watchFile(file, (fileName, eventKind) => callback(fileName, eventKind, path), pollingInterval); } - export function addDirectoryWatcherWithOnlyTriggerLogging(host: System, directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags, log: (s: string) => void): FileWatcher { - const watcherCaption = `DirectoryWatcher ${(flags & WatchDirectoryFlags.Recursive) !== 0 ? "recursive" : ""}:: `; - return createWatcherWithLogging(addDirectoryWatcher, watcherCaption, log, /*logOnlyTrigger*/ true, host, directory, cb, flags); + function watchDirectory(host: System, directory: string, callback: DirectoryWatcherCallback, flags: WatchDirectoryFlags): FileWatcher { + return host.watchDirectory(directory, callback, (flags & WatchDirectoryFlags.Recursive) !== 0); } - type WatchCallback = (fileName: string, cbOptional1?: T, optional?: U) => void; - type AddWatch = (host: System, file: string, cb: WatchCallback, optional?: U) => FileWatcher; - function createWatcherWithLogging(addWatch: AddWatch, watcherCaption: string, log: (s: string) => void, logOnlyTrigger: boolean, host: System, file: string, cb: WatchCallback, optional?: U): FileWatcher { - const info = `PathInfo: ${file}`; - if (!logOnlyTrigger) { - log(`${watcherCaption}Added: ${info}`); + export type WatchCallback = (fileName: string, cbOptional?: T, passThrough?: U) => void; + export type AddWatch = (host: System, file: string, cb: WatchCallback, flags: T, passThrough?: V, detailInfo1?: undefined, detailInfo2?: undefined) => FileWatcher; + export type GetDetailWatchInfo = (detailInfo1: X, detailInfo2: Y) => string; + + type CreateFileWatcher = (host: System, file: string, cb: WatchCallback, flags: T, passThrough: V | undefined, detailInfo1: X | undefined, detailInfo2: Y | undefined, addWatch: AddWatch, log: (s: string) => void, watchCaption: string, getDetailWatchInfo: GetDetailWatchInfo | undefined) => FileWatcher; + function getCreateFileWatcher(watchLogLevel: WatchLogLevel, addWatch: AddWatch): CreateFileWatcher { + switch (watchLogLevel) { + case WatchLogLevel.None: + return addWatch; + case WatchLogLevel.TriggerOnly: + return createFileWatcherWithLogging; + case WatchLogLevel.Verbose: + return createFileWatcherWithTriggerLogging; } - const watcher = addWatch(host, file, (fileName, cbOptional1?) => { - const optionalInfo = cbOptional1 !== undefined ? ` ${cbOptional1}` : ""; - log(`${watcherCaption}Trigger: ${fileName}${optionalInfo} ${info}`); - const start = timestamp(); - cb(fileName, cbOptional1, optional); - const elapsed = timestamp() - start; - log(`${watcherCaption}Elapsed: ${elapsed}ms Trigger: ${fileName}${optionalInfo} ${info}`); - }, optional); + } + + function createFileWatcherWithLogging(host: System, file: string, cb: WatchCallback, flags: T, passThrough: V | undefined, detailInfo1: X | undefined, detailInfo2: Y | undefined, addWatch: AddWatch, log: (s: string) => void, watchCaption: string, getDetailWatchInfo: GetDetailWatchInfo | undefined): FileWatcher { + log(`${watchCaption}:: Added:: ${getWatchInfo(file, flags, detailInfo1, detailInfo2, getDetailWatchInfo)}`); + const watcher = createFileWatcherWithTriggerLogging(host, file, cb, flags, passThrough, detailInfo1, detailInfo2, addWatch, log, watchCaption, getDetailWatchInfo); return { close: () => { - if (!logOnlyTrigger) { - log(`${watcherCaption}Close: ${info}`); - } + log(`${watchCaption}:: Close:: ${getWatchInfo(file, flags, detailInfo1, detailInfo2, getDetailWatchInfo)}`); watcher.close(); } }; } + function createFileWatcherWithTriggerLogging(host: System, file: string, cb: WatchCallback, flags: T, passThrough: V | undefined, detailInfo1: X | undefined, detailInfo2: Y | undefined, addWatch: AddWatch, log: (s: string) => void, watchCaption: string, getDetailWatchInfo: GetDetailWatchInfo | undefined): FileWatcher { + return addWatch(host, file, (fileName, cbOptional) => { + const triggerredInfo = `${watchCaption}:: Triggered with ${fileName}${cbOptional !== undefined ? cbOptional : ""}:: ${getWatchInfo(file, flags, detailInfo1, detailInfo2, getDetailWatchInfo)}`; + log(triggerredInfo); + const start = timestamp(); + cb(fileName, cbOptional, passThrough); + const elapsed = timestamp() - start; + log(`Elapsed:: ${elapsed}ms ${triggerredInfo}`); + }, flags); + } + + function getWatchInfo(file: string, flags: T, detailInfo1: X | undefined, detailInfo2: Y | undefined, getDetailWatchInfo: GetDetailWatchInfo | undefined) { + return `WatchInfo: ${file} ${flags} ${getDetailWatchInfo ? getDetailWatchInfo(detailInfo1, detailInfo2) : ""}` + } + export function closeFileWatcher(watcher: FileWatcher) { watcher.close(); } diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 664938a4cdc..e6ba7fb3894 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -321,9 +321,13 @@ namespace ts.server { typesMapLocation?: string; } - type WatchFile = (host: ServerHost, file: string, cb: FileWatcherCallback, watchType: WatchType, project?: Project) => FileWatcher; - type WatchFilePath = (host: ServerHost, file: string, cb: FilePathWatcherCallback, path: Path, watchType: WatchType, project?: Project) => FileWatcher; - type WatchDirectory = (host: ServerHost, directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags, watchType: WatchType, project?: Project) => FileWatcher; + export type WatchFile = (host: ServerHost, file: string, cb: FileWatcherCallback, pollingInterval: number | undefined, watchType: WatchType, project?: Project) => FileWatcher; + export type WatchFilePath = (host: ServerHost, file: string, cb: FilePathWatcherCallback, pollingInterval: number | undefined, path: Path, watchType: WatchType, project?: Project) => FileWatcher; + export type WatchDirectory = (host: ServerHost, directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags, watchType: WatchType, project?: Project) => FileWatcher; + + function getDetailWatchInfo(watchType: WatchType, project: Project | undefined) { + return `Project: ${project ? project.getProjectName() : ""} WatchType: ${watchType}`; + } export class ProjectService { @@ -450,26 +454,12 @@ namespace ts.server { }; this.documentRegistry = createDocumentRegistry(this.host.useCaseSensitiveFileNames, this.currentDirectory); - if (this.logger.hasLevel(LogLevel.verbose)) { - this.watchFile = (host, file, cb, watchType, project) => ts.addFileWatcherWithLogging(host, file, cb, this.createWatcherLog(watchType, project)); - this.watchFilePath = (host, file, cb, path, watchType, project) => ts.addFilePathWatcherWithLogging(host, file, cb, path, this.createWatcherLog(watchType, project)); - this.watchDirectory = (host, dir, cb, flags, watchType, project) => ts.addDirectoryWatcherWithLogging(host, dir, cb, flags, this.createWatcherLog(watchType, project)); - } - else if (this.logger.loggingEnabled()) { - this.watchFile = (host, file, cb, watchType, project) => ts.addFileWatcherWithOnlyTriggerLogging(host, file, cb, this.createWatcherLog(watchType, project)); - this.watchFilePath = (host, file, cb, path, watchType, project) => ts.addFilePathWatcherWithOnlyTriggerLogging(host, file, cb, path, this.createWatcherLog(watchType, project)); - this.watchDirectory = (host, dir, cb, flags, watchType, project) => ts.addDirectoryWatcherWithOnlyTriggerLogging(host, dir, cb, flags, this.createWatcherLog(watchType, project)); - } - else { - this.watchFile = ts.addFileWatcher; - this.watchFilePath = ts.addFilePathWatcher; - this.watchDirectory = ts.addDirectoryWatcher; - } - } - - private createWatcherLog(watchType: WatchType, project: Project | undefined): (s: string) => void { - const detailedInfo = ` Project: ${project ? project.getProjectName() : ""} WatchType: ${watchType}`; - return s => this.logger.info(s + detailedInfo); + 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.watchFile = createWatchFile(watchLogLevel, log, getDetailWatchInfo); + this.watchFilePath = createWatchFilePath(watchLogLevel, log, getDetailWatchInfo); + this.watchDirectory = createWatchDirectory(watchLogLevel, log, getDetailWatchInfo); } toPath(fileName: string) { @@ -755,8 +745,8 @@ namespace ts.server { } } - private onSourceFileChanged(fileName: NormalizedPath, eventKind: FileWatcherEventKind) { - const info = this.getScriptInfoForNormalizedPath(fileName); + private onSourceFileChanged(fileName: string, eventKind: FileWatcherEventKind, path: Path) { + const info = this.getScriptInfoForPath(path); if (!info) { this.logger.msg(`Error: got watch notification for unknown file: ${fileName}`); } @@ -1136,6 +1126,7 @@ namespace ts.server { this.host, configFileName, (_filename, eventKind) => this.onConfigFileChangeForOpenScriptInfo(configFileName, eventKind), + /*pollingInterval*/ undefined, WatchType.ConfigFileForInferredRoot ); this.logConfigFileWatchUpdate(configFileName, canonicalConfigFilePath, configFileExistenceInfo, ConfigFileWatcherStatus.UpdatedCallback); @@ -1519,6 +1510,7 @@ namespace ts.server { this.host, configFileName, (_fileName, eventKind) => this.onConfigChangedForConfiguredProject(project, eventKind), + /*pollingInterval*/ undefined, WatchType.ConfigFilePath, project ); @@ -1741,10 +1733,12 @@ namespace ts.server { // do not watch files with mixed content - server doesn't know how to interpret it if (!info.isDynamicOrHasMixedContent()) { const { fileName } = info; - info.fileWatcher = this.watchFile( + info.fileWatcher = this.watchFilePath( this.host, fileName, - (_fileName, eventKind) => this.onSourceFileChanged(fileName, eventKind), + (fileName, eventKind, path) => this.onSourceFileChanged(fileName, eventKind, path), + /*pollingInterval*/ undefined, + info.path, WatchType.ClosedScriptInfo ); } diff --git a/src/server/project.ts b/src/server/project.ts index a25e6870bda..48a019dd97e 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -932,6 +932,7 @@ namespace ts.server { this.projectService.delayUpdateProjectGraphAndInferredProjectsRefresh(this); } }, + /*pollingInterval*/ undefined, WatchType.MissingFilePath, this );