mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-29 16:29:19 -05:00
Move the original location getter to ProjectService.
This is in anticipation of work needed to keep original projects alive
This commit is contained in:
@@ -326,6 +326,13 @@ namespace ts.server {
|
||||
syntaxOnly?: boolean;
|
||||
}
|
||||
|
||||
interface OriginalFileInfo { fileName: NormalizedPath; path: Path; }
|
||||
type OpenScriptInfoOrClosedFileInfo = ScriptInfo | OriginalFileInfo;
|
||||
|
||||
function isOpenScriptInfo(infoOrFileName: OpenScriptInfoOrClosedFileInfo): infoOrFileName is ScriptInfo {
|
||||
return !!(infoOrFileName as ScriptInfo).containingProjects;
|
||||
}
|
||||
|
||||
function getDetailWatchInfo(watchType: WatchType, project: Project | undefined) {
|
||||
return `Project: ${project ? project.getProjectName() : ""} WatchType: ${watchType}`;
|
||||
}
|
||||
@@ -1044,12 +1051,12 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
private configFileExists(configFileName: NormalizedPath, canonicalConfigFilePath: string, info: ScriptInfo) {
|
||||
private configFileExists(configFileName: NormalizedPath, canonicalConfigFilePath: string, info: OpenScriptInfoOrClosedFileInfo) {
|
||||
let configFileExistenceInfo = this.configFileExistenceInfoCache.get(canonicalConfigFilePath);
|
||||
if (configFileExistenceInfo) {
|
||||
// By default the info would get impacted by presence of config file since its in the detection path
|
||||
// Only adding the info as a root to inferred project will need the existence to be watched by file watcher
|
||||
if (!configFileExistenceInfo.openFilesImpactedByConfigFile.has(info.path)) {
|
||||
if (isOpenScriptInfo(info) && !configFileExistenceInfo.openFilesImpactedByConfigFile.has(info.path)) {
|
||||
configFileExistenceInfo.openFilesImpactedByConfigFile.set(info.path, false);
|
||||
this.logConfigFileWatchUpdate(configFileName, canonicalConfigFilePath, configFileExistenceInfo, ConfigFileWatcherStatus.OpenFilesImpactedByConfigFileAdd);
|
||||
}
|
||||
@@ -1066,9 +1073,11 @@ namespace ts.server {
|
||||
// Or the whole chain of config files for the roots of the inferred projects
|
||||
|
||||
// Cache the host value of file exists and add the info to map of open files impacted by this config file
|
||||
const openFilesImpactedByConfigFile = createMap<boolean>();
|
||||
openFilesImpactedByConfigFile.set(info.path, false);
|
||||
const exists = this.host.fileExists(configFileName);
|
||||
const openFilesImpactedByConfigFile = createMap<boolean>();
|
||||
if (isOpenScriptInfo(info)) {
|
||||
openFilesImpactedByConfigFile.set(info.path, false);
|
||||
}
|
||||
configFileExistenceInfo = { exists, openFilesImpactedByConfigFile };
|
||||
this.configFileExistenceInfoCache.set(canonicalConfigFilePath, configFileExistenceInfo);
|
||||
this.logConfigFileWatchUpdate(configFileName, canonicalConfigFilePath, configFileExistenceInfo, ConfigFileWatcherStatus.OpenFilesImpactedByConfigFileAdd);
|
||||
@@ -1180,7 +1189,7 @@ namespace ts.server {
|
||||
*/
|
||||
private stopWatchingConfigFilesForClosedScriptInfo(info: ScriptInfo) {
|
||||
Debug.assert(!info.isScriptOpen());
|
||||
this.forEachConfigFileLocation(info, /*infoShouldBeOpen*/ true, (configFileName, canonicalConfigFilePath) => {
|
||||
this.forEachConfigFileLocation(info, (configFileName, canonicalConfigFilePath) => {
|
||||
const configFileExistenceInfo = this.configFileExistenceInfoCache.get(canonicalConfigFilePath);
|
||||
if (configFileExistenceInfo) {
|
||||
const infoIsRootOfInferredProject = configFileExistenceInfo.openFilesImpactedByConfigFile.get(info.path);
|
||||
@@ -1214,7 +1223,7 @@ namespace ts.server {
|
||||
/* @internal */
|
||||
startWatchingConfigFilesForInferredProjectRoot(info: ScriptInfo) {
|
||||
Debug.assert(info.isScriptOpen());
|
||||
this.forEachConfigFileLocation(info, /*infoShouldBeOpen*/ true, (configFileName, canonicalConfigFilePath) => {
|
||||
this.forEachConfigFileLocation(info, (configFileName, canonicalConfigFilePath) => {
|
||||
let configFileExistenceInfo = this.configFileExistenceInfoCache.get(canonicalConfigFilePath);
|
||||
if (!configFileExistenceInfo) {
|
||||
// Create the cache
|
||||
@@ -1242,7 +1251,7 @@ namespace ts.server {
|
||||
*/
|
||||
/* @internal */
|
||||
stopWatchingConfigFilesForInferredProjectRoot(info: ScriptInfo) {
|
||||
this.forEachConfigFileLocation(info, /*infoShouldBeOpen*/ true, (configFileName, canonicalConfigFilePath) => {
|
||||
this.forEachConfigFileLocation(info, (configFileName, canonicalConfigFilePath) => {
|
||||
const configFileExistenceInfo = this.configFileExistenceInfoCache.get(canonicalConfigFilePath);
|
||||
if (configFileExistenceInfo && configFileExistenceInfo.openFilesImpactedByConfigFile.has(info.path)) {
|
||||
Debug.assert(info.isScriptOpen());
|
||||
@@ -1265,12 +1274,12 @@ namespace ts.server {
|
||||
* The server must start searching from the directory containing
|
||||
* the newly opened file.
|
||||
*/
|
||||
private forEachConfigFileLocation(info: ScriptInfo, infoShouldBeOpen: boolean, action: (configFileName: NormalizedPath, canonicalConfigFilePath: string) => boolean | void) {
|
||||
private forEachConfigFileLocation(info: OpenScriptInfoOrClosedFileInfo, action: (configFileName: NormalizedPath, canonicalConfigFilePath: string) => boolean | void) {
|
||||
if (this.syntaxOnly) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
Debug.assert(!infoShouldBeOpen || this.openFiles.has(info.path));
|
||||
Debug.assert(!isOpenScriptInfo(info) || this.openFiles.has(info.path));
|
||||
const projectRootPath = this.openFiles.get(info.path);
|
||||
|
||||
let searchPath = asNormalizedPath(getDirectoryPath(info.fileName));
|
||||
@@ -1309,11 +1318,13 @@ namespace ts.server {
|
||||
* current directory (the directory in which tsc was invoked).
|
||||
* The server must start searching from the directory containing
|
||||
* the newly opened file.
|
||||
* If script info is passed in, it is asserted to be open script info
|
||||
* otherwise just file name
|
||||
*/
|
||||
private getConfigFileNameForFile(info: ScriptInfo, infoShouldBeOpen: boolean) {
|
||||
if (infoShouldBeOpen) Debug.assert(info.isScriptOpen());
|
||||
private getConfigFileNameForFile(info: OpenScriptInfoOrClosedFileInfo) {
|
||||
if (isOpenScriptInfo(info)) Debug.assert(info.isScriptOpen());
|
||||
this.logger.info(`Search path: ${getDirectoryPath(info.fileName)}`);
|
||||
const configFileName = this.forEachConfigFileLocation(info, infoShouldBeOpen, (configFileName, canonicalConfigFilePath) =>
|
||||
const configFileName = this.forEachConfigFileLocation(info, (configFileName, canonicalConfigFilePath) =>
|
||||
this.configFileExists(configFileName, canonicalConfigFilePath, info));
|
||||
if (configFileName) {
|
||||
this.logger.info(`For info: ${info.fileName} :: Config file name: ${configFileName}`);
|
||||
@@ -2005,7 +2016,7 @@ namespace ts.server {
|
||||
// we first detect if there is already a configured project created for it: if so,
|
||||
// we re- read the tsconfig file content and update the project only if we havent already done so
|
||||
// otherwise we create a new one.
|
||||
const configFileName = this.getConfigFileNameForFile(info, /*infoShouldBeOpen*/ true);
|
||||
const configFileName = this.getConfigFileNameForFile(info);
|
||||
if (configFileName) {
|
||||
const project = this.findConfiguredProjectByProjectName(configFileName);
|
||||
if (!project) {
|
||||
@@ -2093,17 +2104,25 @@ namespace ts.server {
|
||||
return this.openClientFileWithNormalizedPath(toNormalizedPath(fileName), fileContent, scriptKind, /*hasMixedContent*/ false, projectRootPath ? toNormalizedPath(projectRootPath) : undefined);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
getProjectForFileWithoutOpening(fileName: NormalizedPath): { readonly scriptInfo: ScriptInfo, readonly projects: ReadonlyArray<Project> } | undefined {
|
||||
const scriptInfo = this.filenameToScriptInfo.get(fileName) ||
|
||||
this.getOrCreateScriptInfoNotOpenedByClientForNormalizedPath(fileName, this.currentDirectory, /*fileContent*/ undefined, /*scriptKind*/ undefined, /*hasMixedContent*/ undefined);
|
||||
if (!scriptInfo) return undefined;
|
||||
if (scriptInfo.containingProjects.length) {
|
||||
return { scriptInfo, projects: scriptInfo.containingProjects };
|
||||
/*@internal*/
|
||||
getOriginalLocationEnsuringConfiguredProject(project: Project, location: sourcemaps.SourceMappableLocation): sourcemaps.SourceMappableLocation | undefined {
|
||||
const originalLocation = project.getSourceMapper().tryGetOriginalLocation(location);
|
||||
if (!originalLocation) return undefined;
|
||||
|
||||
const { fileName } = originalLocation;
|
||||
const originalScriptInfo = this.getScriptInfo(fileName);
|
||||
if (originalScriptInfo && originalScriptInfo.containingProjects.length) {
|
||||
return originalLocation;
|
||||
}
|
||||
const configFileName = this.getConfigFileNameForFile(scriptInfo, /*infoShouldBeOpen*/ false);
|
||||
const project = configFileName === undefined ? undefined : this.findConfiguredProjectByProjectName(configFileName) || this.createConfiguredProject(configFileName);
|
||||
return project && project.containsScriptInfo(scriptInfo) ? { scriptInfo, projects: [project] } : undefined;
|
||||
|
||||
const info: OriginalFileInfo = { fileName: toNormalizedPath(fileName), path: this.toPath(fileName) };
|
||||
const configFileName = this.getConfigFileNameForFile(info);
|
||||
if (!configFileName) return undefined;
|
||||
|
||||
const configuredProject = this.findConfiguredProjectByProjectName(configFileName) || this.createConfiguredProject(configFileName);
|
||||
updateProjectIfDirty(configuredProject);
|
||||
|
||||
return configuredProject.containsFile(info.fileName) ? originalLocation : undefined;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
@@ -2128,7 +2147,7 @@ namespace ts.server {
|
||||
this.openFiles.set(info.path, projectRootPath);
|
||||
let project: ConfiguredProject | ExternalProject | undefined = this.findExternalProjectContainingOpenScriptInfo(info);
|
||||
if (!project && !this.syntaxOnly) { // Checking syntaxOnly is an optimization
|
||||
configFileName = this.getConfigFileNameForFile(info, /*infoShouldBeOpen*/ true);
|
||||
configFileName = this.getConfigFileNameForFile(info);
|
||||
if (configFileName) {
|
||||
project = this.findConfiguredProjectByProjectName(configFileName);
|
||||
if (!project) {
|
||||
|
||||
@@ -711,7 +711,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
containsFile(filename: NormalizedPath, requireOpen?: boolean): boolean {
|
||||
const info = this.projectService.getScriptInfoForPath(this.toPath(filename));
|
||||
const info = this.projectService.getScriptInfoForNormalizedPath(filename);
|
||||
if (info && (info.isScriptOpen() || !requireOpen)) {
|
||||
return this.containsScriptInfo(info);
|
||||
}
|
||||
|
||||
@@ -427,23 +427,20 @@ namespace ts.server {
|
||||
if (projectAndLocation.project.getCancellationToken().isCancellationRequested()) return undefined; // Skip rest of toDo if cancelled
|
||||
cb(projectAndLocation, (project, location) => {
|
||||
seenProjects.set(projectAndLocation.project.projectName, true);
|
||||
const originalLocation = project.getSourceMapper().tryGetOriginalLocation(location);
|
||||
const originalLocation = projectService.getOriginalLocationEnsuringConfiguredProject(project, location);
|
||||
if (!originalLocation) return false;
|
||||
const originalProjectAndScriptInfo = projectService.getProjectForFileWithoutOpening(toNormalizedPath(originalLocation.fileName));
|
||||
if (!originalProjectAndScriptInfo) return false;
|
||||
|
||||
if (originalProjectAndScriptInfo) {
|
||||
toDo = toDo || [];
|
||||
const originalScriptInfo = projectService.getScriptInfo(originalLocation.fileName)!;
|
||||
toDo = toDo || [];
|
||||
|
||||
for (const project of originalProjectAndScriptInfo.projects) {
|
||||
addToTodo({ project, location: originalLocation as TLocation }, toDo, seenProjects);
|
||||
}
|
||||
const symlinkedProjectsMap = projectService.getSymlinkedProjects(originalProjectAndScriptInfo.scriptInfo);
|
||||
if (symlinkedProjectsMap) {
|
||||
symlinkedProjectsMap.forEach((symlinkedProjects) => {
|
||||
for (const symlinkedProject of symlinkedProjects) addToTodo({ project: symlinkedProject, location: originalLocation as TLocation }, toDo!, seenProjects);
|
||||
});
|
||||
}
|
||||
for (const project of originalScriptInfo.containingProjects) {
|
||||
addToTodo({ project, location: originalLocation as TLocation }, toDo, seenProjects);
|
||||
}
|
||||
const symlinkedProjectsMap = projectService.getSymlinkedProjects(originalScriptInfo);
|
||||
if (symlinkedProjectsMap) {
|
||||
symlinkedProjectsMap.forEach((symlinkedProjects) => {
|
||||
for (const symlinkedProject of symlinkedProjects) addToTodo({ project: symlinkedProject, location: originalLocation as TLocation }, toDo!, seenProjects);
|
||||
});
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -9458,7 +9458,7 @@ export function Test2() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Untitled files", () => {
|
||||
describe("tsserverProjectSystem Untitled files", () => {
|
||||
it("Can convert positions to locations", () => {
|
||||
const aTs: File = { path: "/proj/a.ts", content: "" };
|
||||
const tsconfig: File = { path: "/proj/tsconfig.json", content: "{}" };
|
||||
|
||||
Reference in New Issue
Block a user