Skip to content

Commit ebede46

Browse files
committed
Refactor so that its easier to update later
1 parent 78a9bc3 commit ebede46

File tree

2 files changed

+79
-54
lines changed

2 files changed

+79
-54
lines changed

src/server/editorServices.ts

Lines changed: 71 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,7 +1053,7 @@ export class ProjectService {
10531053
/**
10541054
* Open files: with value being project root path, and key being Path of the file that is open
10551055
*/
1056-
readonly openFiles: Map<string, NormalizedPath | undefined> = new Map<Path, NormalizedPath | undefined>();
1056+
readonly openFiles: Map<Path, NormalizedPath | undefined> = new Map<Path, NormalizedPath | undefined>();
10571057
/** @internal */
10581058
readonly configFileForOpenFiles = new Map<Path, NormalizedPath | false>();
10591059
/**
@@ -1364,7 +1364,7 @@ export class ProjectService {
13641364
const event: ProjectsUpdatedInBackgroundEvent = {
13651365
eventName: ProjectsUpdatedInBackgroundEvent,
13661366
data: {
1367-
openFiles: arrayFrom(this.openFiles.keys(), path => this.getScriptInfoForPath(path as Path)!.fileName),
1367+
openFiles: arrayFrom(this.openFiles.keys(), path => this.getScriptInfoForPath(path)!.fileName),
13681368
},
13691369
};
13701370
this.eventHandler(event);
@@ -1797,10 +1797,8 @@ export class ProjectService {
17971797
// Otherwise, we scheduled the update on configured project graph,
17981798
// we would need to schedule the project reload for only the root of inferred projects
17991799
// Get open files to reload projects for
1800-
this.reloadConfiguredProjectForFiles(
1801-
configFileExistenceInfo.openFilesImpactedByConfigFile,
1802-
/*clearSemanticCache*/ false,
1803-
/*delayReload*/ true,
1800+
this.delayReloadConfiguredProjectsForFile(
1801+
configFileExistenceInfo,
18041802
eventKind !== FileWatcherEventKind.Deleted ?
18051803
identity : // Reload open files if they are root of inferred project
18061804
returnTrue, // Reload all the open files impacted by config file
@@ -1809,6 +1807,44 @@ export class ProjectService {
18091807
this.delayEnsureProjectForOpenFiles();
18101808
}
18111809

1810+
/**
1811+
* This function goes through all the openFiles and tries to file the config file for them.
1812+
* If the config file is found and it refers to existing project, it schedules the reload it for reload
1813+
* If there is no existing project it just opens the configured project for the config file
1814+
* shouldReloadProjectFor provides a way to filter out files to reload configured project for
1815+
*/
1816+
private delayReloadConfiguredProjectsForFile(
1817+
configFileExistenceInfo: ConfigFileExistenceInfo,
1818+
shouldReloadProjectFor: (infoIsRootOfInferredProject: boolean) => boolean,
1819+
reason: string,
1820+
) {
1821+
const updatedProjects = new Set<ConfiguredProject>();
1822+
// try to reload config file for all open files
1823+
configFileExistenceInfo.openFilesImpactedByConfigFile?.forEach((infoIsRootOfInferredProject, path) => {
1824+
// Invalidate default config file name for open file
1825+
this.configFileForOpenFiles.delete(path);
1826+
// Filter out the files that need to be ignored
1827+
if (!shouldReloadProjectFor(infoIsRootOfInferredProject)) {
1828+
return;
1829+
}
1830+
const info = this.getScriptInfoForPath(path)!;
1831+
Debug.assert(info.isScriptOpen());
1832+
// This tries to search for a tsconfig.json for the given file. If we found it,
1833+
// we first detect if there is already a configured project created for it: if so,
1834+
// we re- read the tsconfig file content and update the project only if we havent already done so
1835+
// otherwise we create a new one.
1836+
const configFileName = this.getConfigFileNameForFile(info);
1837+
if (configFileName) {
1838+
const project = this.findConfiguredProjectByProjectName(configFileName) || this.createConfiguredProject(configFileName);
1839+
if (tryAddToSet(updatedProjects, project)) {
1840+
project.pendingUpdateLevel = ProgramUpdateLevel.Full;
1841+
project.pendingUpdateReason = reason;
1842+
this.delayUpdateProjectGraph(project);
1843+
}
1844+
}
1845+
});
1846+
}
1847+
18121848
private removeProject(project: Project) {
18131849
this.logger.info("`remove Project::");
18141850
project.print(/*writeProjectFileNames*/ true, /*writeFileExplaination*/ true, /*writeFileVersionAndText*/ false);
@@ -1917,7 +1953,7 @@ export class ProjectService {
19171953
private assignOrphanScriptInfosToInferredProject() {
19181954
// collect orphaned files and assign them to inferred project just like we treat open of a file
19191955
this.openFiles.forEach((projectRootPath, path) => {
1920-
const info = this.getScriptInfoForPath(path as Path)!;
1956+
const info = this.getScriptInfoForPath(path)!;
19211957
// collect all orphaned script infos from open files
19221958
if (info.isOrphan()) {
19231959
this.assignOrphanScriptInfoToInferredProject(info, projectRootPath);
@@ -2319,7 +2355,7 @@ export class ProjectService {
23192355

23202356
this.logger.info("Open files: ");
23212357
this.openFiles.forEach((projectRootPath, path) => {
2322-
const info = this.getScriptInfoForPath(path as Path)!;
2358+
const info = this.getScriptInfoForPath(path)!;
23232359
this.logger.info(`\tFileName: ${info.fileName} ProjectRootPath: ${projectRootPath}`);
23242360
this.logger.info(`\t\tProjects: ${info.containingProjects.map(p => p.getProjectName())}`);
23252361
});
@@ -3510,7 +3546,7 @@ export class ProjectService {
35103546
});
35113547

35123548
// Reload Projects
3513-
this.reloadConfiguredProjectForFiles(this.openFiles as Map<Path, NormalizedPath | undefined>, /*clearSemanticCache*/ true, /*delayReload*/ false, returnTrue, "User requested reload projects");
3549+
this.reloadConfiguredProjectForFiles("User requested reload projects");
35143550
this.externalProjects.forEach(project => {
35153551
this.clearSemanticCache(project);
35163552
project.updateGraph();
@@ -3525,26 +3561,19 @@ export class ProjectService {
35253561
/**
35263562
* This function goes through all the openFiles and tries to file the config file for them.
35273563
* If the config file is found and it refers to existing project, it reloads it either immediately
3528-
* or schedules it for reload depending on delayReload option
35293564
* If there is no existing project it just opens the configured project for the config file
3530-
* reloadForInfo provides a way to filter out files to reload configured project for
35313565
*/
3532-
private reloadConfiguredProjectForFiles<T>(openFiles: Map<Path, T> | undefined, clearSemanticCache: boolean, delayReload: boolean, shouldReloadProjectFor: (openFileValue: T) => boolean, reason: string) {
3533-
const updatedProjects = new Map<string, true>();
3566+
private reloadConfiguredProjectForFiles(reason: string) {
3567+
const updatedProjects = new Set<ConfiguredProject>();
35343568
const reloadChildProject = (child: ConfiguredProject) => {
3535-
if (!updatedProjects.has(child.canonicalConfigFilePath)) {
3536-
updatedProjects.set(child.canonicalConfigFilePath, true);
3537-
this.reloadConfiguredProject(child, reason, /*isInitialLoad*/ false, clearSemanticCache);
3569+
if (tryAddToSet(updatedProjects, child)) {
3570+
this.reloadConfiguredProject(child, reason, /*isInitialLoad*/ false, /*clearSemanticCache*/ true);
35383571
}
35393572
};
35403573
// try to reload config file for all open files
3541-
openFiles?.forEach((openFileValue, path) => {
3574+
this.openFiles?.forEach((_projectRoot, path) => {
35423575
// Invalidate default config file name for open file
35433576
this.configFileForOpenFiles.delete(path);
3544-
// Filter out the files that need to be ignored
3545-
if (!shouldReloadProjectFor(openFileValue)) {
3546-
return;
3547-
}
35483577

35493578
const info = this.getScriptInfoForPath(path)!; // TODO: GH#18217
35503579
Debug.assert(info.isScriptOpen());
@@ -3555,37 +3584,28 @@ export class ProjectService {
35553584
const configFileName = this.getConfigFileNameForFile(info);
35563585
if (configFileName) {
35573586
const project = this.findConfiguredProjectByProjectName(configFileName) || this.createConfiguredProject(configFileName);
3558-
if (!updatedProjects.has(project.canonicalConfigFilePath)) {
3559-
updatedProjects.set(project.canonicalConfigFilePath, true);
3560-
if (delayReload) {
3561-
project.pendingUpdateLevel = ProgramUpdateLevel.Full;
3562-
project.pendingUpdateReason = reason;
3563-
if (clearSemanticCache) this.clearSemanticCache(project);
3564-
this.delayUpdateProjectGraph(project);
3565-
}
3566-
else {
3567-
// reload from the disk
3568-
this.reloadConfiguredProject(project, reason, /*isInitialLoad*/ false, clearSemanticCache);
3569-
// If this project does not contain this file directly, reload the project till the reloaded project contains the script info directly
3570-
if (!projectContainsInfoDirectly(project, info)) {
3571-
const referencedProject = forEachResolvedProjectReferenceProject(
3587+
if (tryAddToSet(updatedProjects, project)) {
3588+
// reload from the disk
3589+
this.reloadConfiguredProject(project, reason, /*isInitialLoad*/ false, /*clearSemanticCache*/ true);
3590+
// If this project does not contain this file directly, reload the project till the reloaded project contains the script info directly
3591+
if (!projectContainsInfoDirectly(project, info)) {
3592+
const referencedProject = forEachResolvedProjectReferenceProject(
3593+
project,
3594+
info.path,
3595+
child => {
3596+
reloadChildProject(child);
3597+
return projectContainsInfoDirectly(child, info);
3598+
},
3599+
ProjectReferenceProjectLoadKind.FindCreate,
3600+
);
3601+
if (referencedProject) {
3602+
// Reload the project's tree that is already present
3603+
forEachResolvedProjectReferenceProject(
35723604
project,
3573-
info.path,
3574-
child => {
3575-
reloadChildProject(child);
3576-
return projectContainsInfoDirectly(child, info);
3577-
},
3578-
ProjectReferenceProjectLoadKind.FindCreate,
3605+
/*fileName*/ undefined,
3606+
reloadChildProject,
3607+
ProjectReferenceProjectLoadKind.Find,
35793608
);
3580-
if (referencedProject) {
3581-
// Reload the project's tree that is already present
3582-
forEachResolvedProjectReferenceProject(
3583-
project,
3584-
/*fileName*/ undefined,
3585-
reloadChildProject,
3586-
ProjectReferenceProjectLoadKind.Find,
3587-
);
3588-
}
35893609
}
35903610
}
35913611
}
@@ -3636,7 +3656,7 @@ export class ProjectService {
36363656
this.printProjects();
36373657

36383658
this.openFiles.forEach((projectRootPath, path) => {
3639-
const info = this.getScriptInfoForPath(path as Path)!;
3659+
const info = this.getScriptInfoForPath(path)!;
36403660
// collect all orphaned script infos from open files
36413661
if (info.isOrphan()) {
36423662
this.assignOrphanScriptInfoToInferredProject(info, projectRootPath);

tests/baselines/reference/api/typescript.d.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3708,7 +3708,7 @@ declare namespace ts {
37083708
/**
37093709
* Open files: with value being project root path, and key being Path of the file that is open
37103710
*/
3711-
readonly openFiles: Map<string, NormalizedPath | undefined>;
3711+
readonly openFiles: Map<Path, NormalizedPath | undefined>;
37123712
/**
37133713
* Map of open files that are opened without complete path but have projectRoot as current directory
37143714
*/
@@ -3777,6 +3777,13 @@ declare namespace ts {
37773777
private delayUpdateSourceInfoProjects;
37783778
private delayUpdateProjectsOfScriptInfoPath;
37793779
private handleDeletedFile;
3780+
/**
3781+
* This function goes through all the openFiles and tries to file the config file for them.
3782+
* If the config file is found and it refers to existing project, it schedules the reload it for reload
3783+
* If there is no existing project it just opens the configured project for the config file
3784+
* shouldReloadProjectFor provides a way to filter out files to reload configured project for
3785+
*/
3786+
private delayReloadConfiguredProjectsForFile;
37803787
private removeProject;
37813788
private assignOrphanScriptInfosToInferredProject;
37823789
/**
@@ -3859,9 +3866,7 @@ declare namespace ts {
38593866
/**
38603867
* This function goes through all the openFiles and tries to file the config file for them.
38613868
* If the config file is found and it refers to existing project, it reloads it either immediately
3862-
* or schedules it for reload depending on delayReload option
38633869
* If there is no existing project it just opens the configured project for the config file
3864-
* reloadForInfo provides a way to filter out files to reload configured project for
38653870
*/
38663871
private reloadConfiguredProjectForFiles;
38673872
/**

0 commit comments

Comments
 (0)