Implementation of watching the failed lookup locations

This commit is contained in:
Sheetal Nandi 2017-08-04 23:09:11 -07:00
parent 65a6ee07e9
commit d55150cbd3
3 changed files with 102 additions and 3 deletions

View File

@ -33,13 +33,20 @@ namespace ts {
export function createResolutionCache(
toPath: (fileName: string) => Path,
getCompilerOptions: () => CompilerOptions,
clearProgramAndScheduleUpdate: () => void,
watchForFailedLookupLocation: (fileName: string, callback: FileWatcherCallback) => FileWatcher,
log: (s: string) => void,
resolveWithGlobalCache?: ResolverWithGlobalCache): ResolutionCache {
let host: ModuleResolutionHost;
let filesWithChangedSetOfUnresolvedImports: Path[];
const resolvedModuleNames = createMap<Map<ResolvedModuleWithFailedLookupLocations>>();
const resolvedTypeReferenceDirectives = createMap<Map<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
type FailedLookupLocationsWatcher = { fileWatcher: FileWatcher; refCount: number };
const failedLookupLocationsWatches = createMap<FailedLookupLocationsWatcher>();
return {
setModuleResolutionHost,
startRecordingFilesWithChangedResolutions,
@ -55,6 +62,11 @@ namespace ts {
}
function clear() {
failedLookupLocationsWatches.forEach((failedLookupLocationWatcher, failedLookupLocationPath: Path) => {
log(`Watcher: FailedLookupLocations: Status: ForceClose: LocationPath: ${failedLookupLocationPath}, refCount: ${failedLookupLocationWatcher.refCount}`);
failedLookupLocationWatcher.fileWatcher.close();
});
failedLookupLocationsWatches.clear();
resolvedModuleNames.clear();
resolvedTypeReferenceDirectives.clear();
}
@ -103,8 +115,9 @@ namespace ts {
}
else {
resolution = loader(name, containingFile, compilerOptions, host);
newResolutions.set(name, resolution);
updateFailedLookupLocationWatches(containingFile, name, existingResolution && existingResolution.failedLookupLocations, resolution.failedLookupLocations);
}
newResolutions.set(name, resolution);
if (logChanges && filesWithChangedSetOfUnresolvedImports && !resolutionIsEqualTo(existingResolution, resolution)) {
filesWithChangedSetOfUnresolvedImports.push(path);
// reset log changes to avoid recording the same file multiple times
@ -155,7 +168,6 @@ namespace ts {
}
}
function resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[] {
return resolveNamesWithLocalCache(typeDirectiveNames, containingFile, resolvedTypeReferenceDirectives, resolveTypeReferenceDirective,
m => m.resolvedTypeReferenceDirective, r => r.resolvedFileName, /*logChanges*/ false);
@ -166,6 +178,75 @@ namespace ts {
m => m.resolvedModule, r => r.resolvedFileName, logChanges);
}
function watchFailedLookupLocation(failedLookupLocation: string, failedLookupLocationPath: Path, containingFile: string, name: string) {
const failedLookupLocationWatcher = failedLookupLocationsWatches.get(failedLookupLocationPath);
if (failedLookupLocationWatcher) {
failedLookupLocationWatcher.refCount++;
log(`Watcher: FailedLookupLocations: Status: Using existing watcher: Location: ${failedLookupLocation}, containingFile: ${containingFile}, name: ${name} refCount: ${failedLookupLocationWatcher.refCount}`);
}
else {
const fileWatcher = watchForFailedLookupLocation(failedLookupLocation, (__fileName, eventKind) => {
log(`Watcher: FailedLookupLocations: Status: ${FileWatcherEventKind[eventKind]}: Location: ${failedLookupLocation}, containingFile: ${containingFile}, name: ${name}`);
// There is some kind of change in the failed lookup location, update the program
clearProgramAndScheduleUpdate();
});
failedLookupLocationsWatches.set(failedLookupLocationPath, { fileWatcher, refCount: 1 });
}
}
function closeFailedLookupLocationWatcher(failedLookupLocation: string, failedLookupLocationPath: Path, containingFile: string, name: string) {
const failedLookupLocationWatcher = failedLookupLocationsWatches.get(failedLookupLocationPath);
Debug.assert(!!failedLookupLocationWatcher);
failedLookupLocationWatcher.refCount--;
if (failedLookupLocationWatcher.refCount) {
log(`Watcher: FailedLookupLocations: Status: Removing existing watcher: Location: ${failedLookupLocation}, containingFile: ${containingFile}, name: ${name}: refCount: ${failedLookupLocationWatcher.refCount}`);
}
else {
log(`Watcher: FailedLookupLocations: Status: Closing the file watcher: Location: ${failedLookupLocation}, containingFile: ${containingFile}, name: ${name}`);
failedLookupLocationWatcher.fileWatcher.close();
failedLookupLocationsWatches.delete(failedLookupLocationPath);
}
}
type FailedLookupLocationAction = (failedLookupLocation: string, failedLookupLocationPath: Path, containingFile: string, name: string) => void;
function withFailedLookupLocations(failedLookupLocations: string[], containingFile: string, name: string, fn: FailedLookupLocationAction) {
forEach(failedLookupLocations, failedLookupLocation => {
fn(failedLookupLocation, toPath(failedLookupLocation), containingFile, name);
});
}
function updateFailedLookupLocationWatches(containingFile: string, name: string, existingFailedLookupLocations: string[], failedLookupLocations: string[]) {
if (failedLookupLocations) {
if (existingFailedLookupLocations) {
const existingWatches = arrayToMap(existingFailedLookupLocations, failedLookupLocation => toPath(failedLookupLocation));
for (const failedLookupLocation of failedLookupLocations) {
const failedLookupLocationPath = toPath(failedLookupLocation);
if (existingWatches && existingWatches.has(failedLookupLocationPath)) {
// still has same failed lookup location, keep the was
existingWatches.delete(failedLookupLocationPath);
}
else {
// Create new watch
watchFailedLookupLocation(failedLookupLocation, failedLookupLocationPath, containingFile, name);
}
}
// Close all the watches that are still present in the existingWatches since those are not the locations looked up for buy new resolution
existingWatches.forEach((failedLookupLocation, failedLookupLocationPath: Path) =>
closeFailedLookupLocationWatcher(failedLookupLocation, failedLookupLocationPath, containingFile, name)
);
}
else {
// Watch all the failed lookup locations
withFailedLookupLocations(failedLookupLocations, containingFile, name, watchFailedLookupLocation);
}
}
else {
// Close existing watches for the failed locations
withFailedLookupLocations(existingFailedLookupLocations, containingFile, name, closeFailedLookupLocationWatcher);
}
}
function invalidateResolutionCacheOfDeletedFile<T extends NameResolutionWithFailedLookupLocations, R>(
deletedFilePath: Path,
cache: Map<Map<T>>,
@ -174,6 +255,9 @@ namespace ts {
cache.forEach((value, path) => {
if (path === deletedFilePath) {
cache.delete(path);
value.forEach((resolution, name) => {
withFailedLookupLocations(resolution.failedLookupLocations, path, name, closeFailedLookupLocationWatcher);
});
}
else if (value) {
value.forEach((resolution) => {

View File

@ -271,7 +271,13 @@ namespace ts {
const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames);
// Cache for the module resolution
const resolutionCache = createResolutionCache(fileName => toPath(fileName), () => compilerOptions);
const resolutionCache = createResolutionCache(
fileName => toPath(fileName),
() => compilerOptions,
() => clearExistingProgramAndScheduleProgramUpdate(),
(fileName, callback) => system.watchFile(fileName, callback),
s => writeLog(s)
);
// There is no extra check needed since we can just rely on the program to decide emit
const builder = createBuilder(getCanonicalFileName, getFileEmitOutput, computeHash, _sourceFile => true);
@ -465,6 +471,11 @@ namespace ts {
scheduleProgramUpdate();
}
function clearExistingProgramAndScheduleProgramUpdate() {
program = undefined;
scheduleProgramUpdate();
}
function updateProgram() {
timerToUpdateProgram = undefined;
reportWatchDiagnostic(createCompilerDiagnostic(Diagnostics.File_change_detected_Starting_incremental_compilation));

View File

@ -217,6 +217,9 @@ namespace ts.server {
this.resolutionCache = createResolutionCache(
fileName => this.projectService.toPath(fileName),
() => this.compilerOptions,
() => this.markAsDirty(),
(fileName, callback) => host.watchFile(fileName, callback),
s => this.projectService.logger.info(s),
(primaryResult, moduleName, compilerOptions, host) => resolveWithGlobalCache(primaryResult, moduleName, compilerOptions, host,
this.getTypeAcquisition().enable ? this.projectService.typingsInstaller.globalTypingsCacheLocation : undefined, this.getProjectName())
);
@ -359,6 +362,7 @@ namespace ts.server {
this.rootFilesMap = undefined;
this.program = undefined;
this.builder = undefined;
this.resolutionCache.clear();
this.resolutionCache = undefined;
this.cachedUnresolvedImportsPerFile = undefined;
this.projectErrors = undefined;