Dont depend on project in document position mapper so that we can unload or remove projects independently

This commit is contained in:
Sheetal Nandi 2018-11-16 11:24:01 -08:00
parent 12428d45c0
commit afdf1e90ec
4 changed files with 78 additions and 66 deletions

View File

@ -2195,6 +2195,70 @@ namespace ts.server {
return this.filenameToScriptInfo.get(fileName);
}
/*@internal*/
getDocumentPositionMapper(fileName: string, project: Project): DocumentPositionMapper | undefined {
const declarationInfo = this.getOrCreateScriptInfoNotOpenedByClient(fileName, project.currentDirectory, project.directoryStructureHost);
if (!declarationInfo) return undefined;
declarationInfo.getSnapshot(); // Ensure synchronized
const existingMapper = declarationInfo.textStorage.mapper;
if (existingMapper !== undefined) {
return existingMapper ? existingMapper : undefined;
}
// Create the mapper
declarationInfo.mapInfo = undefined;
let readMapFile: ((fileName: string) => string | undefined) | undefined = fileName => {
const mapInfo = this.getOrCreateScriptInfoNotOpenedByClient(fileName, project.currentDirectory, project.directoryStructureHost);
if (!mapInfo) return undefined;
declarationInfo.mapInfo = mapInfo;
const snap = mapInfo.getSnapshot();
return snap.getText(0, snap.getLength());
};
const projectName = project.projectName;
const mapper = getDocumentPositionMapper(
{ getCanonicalFileName: this.toCanonicalFileName, log: s => this.logger.info(s), getSourceFileLike: f => this.getSourceFileLike(f, projectName) },
declarationInfo.fileName,
declarationInfo.textStorage.getLineInfo(),
readMapFile
);
readMapFile = undefined; // Remove ref to project
declarationInfo.textStorage.mapper = mapper || false;
return mapper;
}
/*@internal*/
getSourceFileLike(fileName: string, projectName: string | Project) {
const project = (projectName as Project).projectName ? projectName as Project : this.findProject(projectName as string);
if (project) {
const path = project.toPath(fileName);
const sourceFile = project.getSourceFile(path);
if (sourceFile && sourceFile.resolvedPath === path) return sourceFile;
}
// Need to look for other files.
const info = this.getOrCreateScriptInfoNotOpenedByClient(fileName, (project || this).currentDirectory, project ? project.directoryStructureHost : this.host);
if (!info) return undefined;
// Key doesnt matter since its only for text and lines
if (info.cacheSourceFile) return info.cacheSourceFile.sourceFile;
if (info.textStorage.sourceFileLike) return info.textStorage.sourceFileLike;
info.textStorage.sourceFileLike = {
get text() {
Debug.fail("shouldnt need text");
return "";
},
getLineAndCharacterOfPosition: pos => {
const lineOffset = info.positionToLineOffset(pos);
return { line: lineOffset.line - 1, character: lineOffset.offset - 1 };
},
getPositionOfLineAndCharacter: (line, character) => info.lineOffsetToPosition(line + 1, character + 1)
};
return info.textStorage.sourceFileLike;
}
setHostConfiguration(args: protocol.ConfigureRequestArguments) {
if (args.file) {
const info = this.getScriptInfoForNormalizedPath(toNormalizedPath(args.file));

View File

@ -505,62 +505,12 @@ namespace ts.server {
/*@internal*/
getDocumentPositionMapper(fileName: string): DocumentPositionMapper | undefined {
const declarationInfo = this.projectService.getOrCreateScriptInfoNotOpenedByClient(fileName, this.currentDirectory, this.directoryStructureHost);
if (!declarationInfo) return undefined;
declarationInfo.getSnapshot(); // Ensure synchronized
const existingMapper = declarationInfo.textStorage.mapper;
if (existingMapper !== undefined) {
return existingMapper ? existingMapper : undefined;
}
// Create the mapper
declarationInfo.mapInfo = undefined;
const mapper = getDocumentPositionMapper({
getCanonicalFileName: this.projectService.toCanonicalFileName,
log: s => this.log(s),
readMapFile: f => this.readMapFile(f, declarationInfo),
getSourceFileLike: f => this.getSourceFileLike(f)
}, declarationInfo.fileName, declarationInfo.textStorage.getLineInfo());
declarationInfo.textStorage.mapper = mapper || false;
return mapper;
}
private readMapFile(fileName: string, declarationInfo: ScriptInfo) {
const mapInfo = this.projectService.getOrCreateScriptInfoNotOpenedByClient(fileName, this.currentDirectory, this.directoryStructureHost);
if (!mapInfo) return undefined;
declarationInfo.mapInfo = mapInfo;
const snap = mapInfo.getSnapshot();
return snap.getText(0, snap.getLength());
return this.projectService.getDocumentPositionMapper(fileName, this);
}
/*@internal*/
getSourceFileLike(fileName: string) {
const path = this.toPath(fileName);
const sourceFile = this.getSourceFile(path);
if (sourceFile && sourceFile.resolvedPath === path) return sourceFile;
// Need to look for other files.
const info = this.projectService.getOrCreateScriptInfoNotOpenedByClient(fileName, this.currentDirectory, this.directoryStructureHost);
if (!info) return undefined;
// Key doesnt matter since its only for text and lines
if (info.cacheSourceFile) return info.cacheSourceFile.sourceFile;
if (info.textStorage.sourceFileLike) return info.textStorage.sourceFileLike;
info.textStorage.sourceFileLike = {
get text() {
Debug.fail("shouldnt need text");
return "";
},
getLineAndCharacterOfPosition: pos => {
const lineOffset = info.positionToLineOffset(pos);
return { line: lineOffset.line - 1, character: lineOffset.offset - 1 };
},
getPositionOfLineAndCharacter: (line, character) => info.lineOffsetToPosition(line + 1, character + 1)
};
return info.textStorage.sourceFileLike;
return this.projectService.getSourceFileLike(fileName, this);
}
private shouldEmitFile(scriptInfo: ScriptInfo) {

View File

@ -42,12 +42,12 @@ namespace ts {
}
else if (host.readFile) {
const file = getSourceFileLike(fileName);
mapper = file && ts.getDocumentPositionMapper({
getSourceFileLike,
getCanonicalFileName,
log: s => host.log(s),
readMapFile: f => !host.fileExists || host.fileExists(f) ? host.readFile!(f) : undefined
}, fileName, getLineInfo(file.text, getLineStarts(file)));
mapper = file && ts.getDocumentPositionMapper(
{ getSourceFileLike, getCanonicalFileName, log: s => host.log(s) },
fileName,
getLineInfo(file.text, getLineStarts(file)),
f => !host.fileExists || host.fileExists(f) ? host.readFile!(f) : undefined
);
}
documentPositionMappers.set(path, mapper || identitySourceMapConsumer);
return mapper || identitySourceMapConsumer;
@ -118,7 +118,6 @@ namespace ts {
}
function toLineColumnOffset(fileName: string, position: number): LineAndCharacter {
// TODO:: shkamat
const file = getSourceFileLike(fileName)!; // TODO: GH#18217
return file.getLineAndCharacterOfPosition(position);
}
@ -129,11 +128,11 @@ namespace ts {
}
}
export interface GetDocumentPositionMapperHost extends DocumentPositionMapperHost {
readMapFile(fileName: string): string | undefined;
}
export function getDocumentPositionMapper(host: GetDocumentPositionMapperHost, generatedFileName: string, generatedFileLineInfo: LineInfo) {
export function getDocumentPositionMapper(
host: DocumentPositionMapperHost,
generatedFileName: string,
generatedFileLineInfo: LineInfo,
readMapFile: (fileName: string) => string | undefined) {
let mapFileName = tryGetSourceMappingURL(generatedFileLineInfo);
if (mapFileName) {
const match = base64UrlRegExp.exec(mapFileName);
@ -153,7 +152,7 @@ namespace ts {
possibleMapLocations.push(generatedFileName + ".map");
for (const location of possibleMapLocations) {
const mapFileName = getNormalizedAbsolutePath(location, getDirectoryPath(generatedFileName));
const mapFileContents = host.readMapFile(mapFileName);
const mapFileContents = readMapFile(mapFileName);
if (mapFileContents) {
return convertDocumentToSourceMapper(host, mapFileContents, mapFileName);
}

View File

@ -8210,7 +8210,6 @@ declare namespace ts.server {
getGlobalProjectErrors(): ReadonlyArray<Diagnostic>;
getAllProjectErrors(): ReadonlyArray<Diagnostic>;
getLanguageService(ensureSynchronized?: boolean): LanguageService;
private readMapFile;
private shouldEmitFile;
getCompileOnSaveAffectedFileList(scriptInfo: ScriptInfo): string[];
/**