diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index d98aa948113..5c70c7be8bf 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -2197,8 +2197,8 @@ namespace ts.server { } /*@internal*/ - getDocumentPositionMapper(fileName: string, project: Project): DocumentPositionMapper | undefined { - const declarationInfo = this.getOrCreateScriptInfoNotOpenedByClient(fileName, project.currentDirectory, project.directoryStructureHost); + getDocumentPositionMapper(project: Project, generatedFileName: string, sourceFileName?: string): DocumentPositionMapper | undefined { + const declarationInfo = this.getOrCreateScriptInfoNotOpenedByClient(generatedFileName, project.currentDirectory, project.directoryStructureHost); if (!declarationInfo) return undefined; declarationInfo.getSnapshot(); // Ensure synchronized @@ -2210,7 +2210,6 @@ namespace ts.server { // Create the mapper declarationInfo.mapInfo = undefined; - // TODO: shkamat Lifetime of declarationInfo and mapInfo let readMapFile: ((fileName: string) => string | undefined) | undefined = fileName => { const mapInfo = this.getOrCreateScriptInfoNotOpenedByClient(fileName, project.currentDirectory, project.directoryStructureHost); if (!mapInfo) return undefined; @@ -2227,6 +2226,11 @@ namespace ts.server { ); readMapFile = undefined; // Remove ref to project declarationInfo.mapper = mapper || false; + if (sourceFileName && mapper) { + // Attach as source + const sourceInfo = this.getOrCreateScriptInfoNotOpenedByClient(sourceFileName, project.currentDirectory, project.directoryStructureHost)!; + (declarationInfo.mapInfo!.sourceInfos || (declarationInfo.mapInfo!.sourceInfos = createMap())).set(sourceInfo.path, true); + } return mapper; } @@ -2609,13 +2613,25 @@ namespace ts.server { private removeOrphanScriptInfos() { const toRemoveScriptInfos = cloneMap(this.filenameToScriptInfo); this.filenameToScriptInfo.forEach(info => { - if (info.isScriptOpen() || !info.isOrphan()) { - toRemoveScriptInfos.delete(info.path); - if (info.mapInfo) { - toRemoveScriptInfos.delete(info.mapInfo.path); - if (info.mapInfo.sourceInfos) { - info.mapInfo.sourceInfos.forEach((_value, path) => toRemoveScriptInfos.delete(path)); - } + // If script info is open or orphan, retain it and its dependencies + if (!info.isScriptOpen() && info.isOrphan()) { + // Otherwise if there is any source info that is alive, this alive too + if (!info.mapInfo || !info.mapInfo.sourceInfos) return; + if (!forEachKey(info.mapInfo.sourceInfos, path => { + const info = this.getScriptInfoForPath(path as Path)!; + return info.isScriptOpen() || !info.isOrphan(); + })) { + return; + } + } + + // Retain this script info + toRemoveScriptInfos.delete(info.path); + if (info.mapInfo) { + // And map file info and source infos + toRemoveScriptInfos.delete(info.mapInfo.path); + if (info.mapInfo.sourceInfos) { + info.mapInfo.sourceInfos.forEach((_value, path) => toRemoveScriptInfos.delete(path)); } } }); diff --git a/src/server/project.ts b/src/server/project.ts index 6ea9589cb4b..a28e9c35be5 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -504,8 +504,8 @@ namespace ts.server { } /*@internal*/ - getDocumentPositionMapper(fileName: string): DocumentPositionMapper | undefined { - return this.projectService.getDocumentPositionMapper(fileName, this); + getDocumentPositionMapper(generatedFileName: string, sourceFileName?: string): DocumentPositionMapper | undefined { + return this.projectService.getDocumentPositionMapper(this, generatedFileName, sourceFileName); } /*@internal*/ diff --git a/src/server/scriptInfo.ts b/src/server/scriptInfo.ts index 3d8c737064f..d2f230178d3 100644 --- a/src/server/scriptInfo.ts +++ b/src/server/scriptInfo.ts @@ -64,14 +64,20 @@ namespace ts.server { this.switchToScriptVersionCache(); } + private resetSourceMapInfo() { + this.info.mapper = undefined; + this.info.sourceFileLike = undefined; + this.info.mapInfo = undefined; + this.info.sourceInfos = undefined; + } + /** Public for testing */ public useText(newText?: string) { this.svc = undefined; this.text = newText; this.lineMap = undefined; this.fileSize = undefined; - this.info.mapper = undefined; - this.info.sourceFileLike = undefined; + this.resetSourceMapInfo(); this.version.text++; } @@ -81,8 +87,7 @@ namespace ts.server { this.text = undefined; this.lineMap = undefined; this.fileSize = undefined; - this.info.mapper = undefined; - this.info.sourceFileLike = undefined; + this.resetSourceMapInfo(); } /** @@ -304,7 +309,7 @@ namespace ts.server { /*@internal*/ sourceInfos?: Map; /*@internal*/ - mapper: DocumentPositionMapper | false | undefined = false; + mapper?: DocumentPositionMapper | false; /*@internal*/ sourceFileLike: SourceFileLike | undefined; diff --git a/src/services/services.ts b/src/services/services.ts index a0c41ffe94d..e8dfb8640e0 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1145,7 +1145,7 @@ namespace ts { getProgram, fileExists: host.fileExists && (f => host.fileExists!(f)), readFile: host.readFile && ((f, encoding) => host.readFile!(f, encoding)), - getDocumentPositionMapper: host.getDocumentPositionMapper && (f => host.getDocumentPositionMapper!(f)), + getDocumentPositionMapper: host.getDocumentPositionMapper && ((generatedFileName, sourceFileName) => host.getDocumentPositionMapper!(generatedFileName, sourceFileName)), getSourceFileLike: host.getSourceFileLike && (f => host.getSourceFileLike!(f)), log }); diff --git a/src/services/sourcemaps.ts b/src/services/sourcemaps.ts index 00d2696abf5..ce692fc2184 100644 --- a/src/services/sourcemaps.ts +++ b/src/services/sourcemaps.ts @@ -16,7 +16,7 @@ namespace ts { fileExists?(path: string): boolean; readFile?(path: string, encoding?: string): string | undefined; getSourceFileLike?(fileName: string): SourceFileLike | undefined; - getDocumentPositionMapper?(fileName: string): DocumentPositionMapper | undefined; + getDocumentPositionMapper?(generatedFileName: string, sourceFileName?: string): DocumentPositionMapper | undefined; log(s: string): void; } @@ -31,20 +31,20 @@ namespace ts { return ts.toPath(fileName, currentDirectory, getCanonicalFileName); } - function getDocumentPositionMapper(fileName: string) { - const path = toPath(fileName); + function getDocumentPositionMapper(generatedFileName: string, sourceFileName?: string) { + const path = toPath(generatedFileName); const value = documentPositionMappers.get(path); if (value) return value; let mapper: DocumentPositionMapper | undefined; if (host.getDocumentPositionMapper) { - mapper = host.getDocumentPositionMapper(fileName); + mapper = host.getDocumentPositionMapper(generatedFileName, sourceFileName); } else if (host.readFile) { - const file = getSourceFileLike(fileName); + const file = getSourceFileLike(generatedFileName); mapper = file && ts.getDocumentPositionMapper( { getSourceFileLike, getCanonicalFileName, log: s => host.log(s) }, - fileName, + generatedFileName, getLineInfo(file.text, getLineStarts(file)), f => !host.fileExists || host.fileExists(f) ? host.readFile!(f) : undefined ); @@ -78,7 +78,7 @@ namespace ts { getDeclarationEmitOutputFilePathWorker(info.fileName, program.getCompilerOptions(), currentDirectory, program.getCommonSourceDirectory(), getCanonicalFileName); if (declarationPath === undefined) return undefined; - const newLoc = getDocumentPositionMapper(declarationPath).getGeneratedPosition(info); + const newLoc = getDocumentPositionMapper(declarationPath, info.fileName).getGeneratedPosition(info); return newLoc === info ? undefined : newLoc; } diff --git a/src/services/types.ts b/src/services/types.ts index d3a8f8cdd52..5100a999306 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -234,7 +234,7 @@ namespace ts { writeFile?(fileName: string, content: string): void; /* @internal */ - getDocumentPositionMapper?(fileName: string): DocumentPositionMapper | undefined; + getDocumentPositionMapper?(generatedFileName: string, sourceFileName?: string): DocumentPositionMapper | undefined; /* @internal */ getSourceFileLike?(fileName: string): SourceFileLike | undefined; } diff --git a/src/testRunner/unittests/tsserverProjectSystem.ts b/src/testRunner/unittests/tsserverProjectSystem.ts index 217149efd88..59a6f6019d1 100644 --- a/src/testRunner/unittests/tsserverProjectSystem.ts +++ b/src/testRunner/unittests/tsserverProjectSystem.ts @@ -10757,7 +10757,6 @@ fn5();` openFilesForSession([dependencyTs, randomFile], session); checkNumberOfProjects(service, { configuredProjects: 2 }); checkProjectActualFiles(service.configuredProjects.get(dependencyConfig.path)!, [dependencyTs.path, libFile.path, dependencyConfig.path]); - debugger; for (let i = 0; i < 5; i++) { const startSpan = { line: i + 1, offset: 17 }; const response = session.executeCommandSeq({