Skip to content

Commit a6a27e9

Browse files
authored
Merge pull request microsoft#27577 from Microsoft/projectLoadEvent
Send event for ProjectLoadStart and ProjectLoadFinish
2 parents 6e5f477 + 37e25c8 commit a6a27e9

File tree

6 files changed

+336
-73
lines changed

6 files changed

+336
-73
lines changed

src/server/editorServices.ts

Lines changed: 71 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ namespace ts.server {
55

66
// tslint:disable variable-name
77
export const ProjectsUpdatedInBackgroundEvent = "projectsUpdatedInBackground";
8+
export const ProjectLoadingStartEvent = "projectLoadingStart";
9+
export const ProjectLoadingFinishEvent = "projectLoadingFinish";
810
export const SurveyReady = "surveyReady";
911
export const LargeFileReferencedEvent = "largeFileReferenced";
1012
export const ConfigFileDiagEvent = "configFileDiag";
@@ -18,6 +20,16 @@ namespace ts.server {
1820
data: { openFiles: string[]; };
1921
}
2022

23+
export interface ProjectLoadingStartEvent {
24+
eventName: typeof ProjectLoadingStartEvent;
25+
data: { project: Project; reason: string; };
26+
}
27+
28+
export interface ProjectLoadingFinishEvent {
29+
eventName: typeof ProjectLoadingFinishEvent;
30+
data: { project: Project; };
31+
}
32+
2133
export interface SurveyReady {
2234
eventName: typeof SurveyReady;
2335
data: { surveyId: string; };
@@ -122,7 +134,15 @@ namespace ts.server {
122134
readonly checkJs: boolean;
123135
}
124136

125-
export type ProjectServiceEvent = LargeFileReferencedEvent | SurveyReady | ProjectsUpdatedInBackgroundEvent | ConfigFileDiagEvent | ProjectLanguageServiceStateEvent | ProjectInfoTelemetryEvent | OpenFileInfoTelemetryEvent;
137+
export type ProjectServiceEvent = LargeFileReferencedEvent |
138+
SurveyReady |
139+
ProjectsUpdatedInBackgroundEvent |
140+
ProjectLoadingStartEvent |
141+
ProjectLoadingFinishEvent |
142+
ConfigFileDiagEvent |
143+
ProjectLanguageServiceStateEvent |
144+
ProjectInfoTelemetryEvent |
145+
OpenFileInfoTelemetryEvent;
126146

127147
export type ProjectServiceEventHandler = (event: ProjectServiceEvent) => void;
128148

@@ -721,6 +741,33 @@ namespace ts.server {
721741
this.eventHandler(event);
722742
}
723743

744+
/* @internal */
745+
sendProjectLoadingStartEvent(project: ConfiguredProject, reason: string) {
746+
if (!this.eventHandler) {
747+
return;
748+
}
749+
project.sendLoadingProjectFinish = true;
750+
const event: ProjectLoadingStartEvent = {
751+
eventName: ProjectLoadingStartEvent,
752+
data: { project, reason }
753+
};
754+
this.eventHandler(event);
755+
}
756+
757+
/* @internal */
758+
sendProjectLoadingFinishEvent(project: ConfiguredProject) {
759+
if (!this.eventHandler || !project.sendLoadingProjectFinish) {
760+
return;
761+
}
762+
763+
project.sendLoadingProjectFinish = false;
764+
const event: ProjectLoadingFinishEvent = {
765+
eventName: ProjectLoadingFinishEvent,
766+
data: { project }
767+
};
768+
this.eventHandler(event);
769+
}
770+
724771
/* @internal */
725772
delayUpdateProjectGraphAndEnsureProjectStructureForOpenFiles(project: Project) {
726773
this.delayUpdateProjectGraph(project);
@@ -955,6 +1002,7 @@ namespace ts.server {
9551002
else {
9561003
this.logConfigFileWatchUpdate(project.getConfigFilePath(), project.canonicalConfigFilePath, configFileExistenceInfo, ConfigFileWatcherStatus.ReloadingInferredRootFiles);
9571004
project.pendingReload = ConfigFileProgramReloadLevel.Full;
1005+
project.pendingReloadReason = "Change in config file detected";
9581006
this.delayUpdateProjectGraph(project);
9591007
// As we scheduled the update on configured project graph,
9601008
// we would need to schedule the project reload for only the root of inferred projects
@@ -1613,22 +1661,23 @@ namespace ts.server {
16131661
}
16141662

16151663
/* @internal */
1616-
private createConfiguredProjectWithDelayLoad(configFileName: NormalizedPath) {
1664+
private createConfiguredProjectWithDelayLoad(configFileName: NormalizedPath, reason: string) {
16171665
const project = this.createConfiguredProject(configFileName);
16181666
project.pendingReload = ConfigFileProgramReloadLevel.Full;
1667+
project.pendingReloadReason = reason;
16191668
return project;
16201669
}
16211670

16221671
/* @internal */
1623-
private createAndLoadConfiguredProject(configFileName: NormalizedPath) {
1672+
private createAndLoadConfiguredProject(configFileName: NormalizedPath, reason: string) {
16241673
const project = this.createConfiguredProject(configFileName);
1625-
this.loadConfiguredProject(project);
1674+
this.loadConfiguredProject(project, reason);
16261675
return project;
16271676
}
16281677

16291678
/* @internal */
1630-
private createLoadAndUpdateConfiguredProject(configFileName: NormalizedPath) {
1631-
const project = this.createAndLoadConfiguredProject(configFileName);
1679+
private createLoadAndUpdateConfiguredProject(configFileName: NormalizedPath, reason: string) {
1680+
const project = this.createAndLoadConfiguredProject(configFileName, reason);
16321681
project.updateGraph();
16331682
return project;
16341683
}
@@ -1637,7 +1686,9 @@ namespace ts.server {
16371686
* Read the config file of the project, and update the project root file names.
16381687
*/
16391688
/* @internal */
1640-
private loadConfiguredProject(project: ConfiguredProject) {
1689+
private loadConfiguredProject(project: ConfiguredProject, reason: string) {
1690+
this.sendProjectLoadingStartEvent(project, reason);
1691+
16411692
// Read updated contents from disk
16421693
const configFilename = normalizePath(project.getConfigFilePath());
16431694

@@ -1776,7 +1827,7 @@ namespace ts.server {
17761827
* Read the config file of the project again by clearing the cache and update the project graph
17771828
*/
17781829
/* @internal */
1779-
reloadConfiguredProject(project: ConfiguredProject) {
1830+
reloadConfiguredProject(project: ConfiguredProject, reason: string) {
17801831
// At this point, there is no reason to not have configFile in the host
17811832
const host = project.getCachedDirectoryStructureHost();
17821833

@@ -1786,7 +1837,7 @@ namespace ts.server {
17861837
this.logger.info(`Reloading configured project ${configFileName}`);
17871838

17881839
// Load project from the disk
1789-
this.loadConfiguredProject(project);
1840+
this.loadConfiguredProject(project, reason);
17901841
project.updateGraph();
17911842

17921843
this.sendConfigFileDiagEvent(project, configFileName);
@@ -2139,7 +2190,6 @@ namespace ts.server {
21392190
if (project.hasExternalProjectRef() &&
21402191
project.pendingReload === ConfigFileProgramReloadLevel.Full &&
21412192
!this.pendingProjectUpdates.has(project.getProjectName())) {
2142-
this.loadConfiguredProject(project);
21432193
project.updateGraph();
21442194
}
21452195
});
@@ -2171,7 +2221,7 @@ namespace ts.server {
21712221
// as there is no need to load contents of the files from the disk
21722222

21732223
// Reload Projects
2174-
this.reloadConfiguredProjectForFiles(this.openFiles, /*delayReload*/ false, returnTrue);
2224+
this.reloadConfiguredProjectForFiles(this.openFiles, /*delayReload*/ false, returnTrue, "User requested reload projects");
21752225
this.ensureProjectForOpenFiles();
21762226
}
21772227

@@ -2182,7 +2232,8 @@ namespace ts.server {
21822232
/*delayReload*/ true,
21832233
ignoreIfNotRootOfInferredProject ?
21842234
isRootOfInferredProject => isRootOfInferredProject : // Reload open files if they are root of inferred project
2185-
returnTrue // Reload all the open files impacted by config file
2235+
returnTrue, // Reload all the open files impacted by config file
2236+
"Change in config file detected"
21862237
);
21872238
this.delayEnsureProjectForOpenFiles();
21882239
}
@@ -2194,7 +2245,7 @@ namespace ts.server {
21942245
* If the there is no existing project it just opens the configured project for the config file
21952246
* reloadForInfo provides a way to filter out files to reload configured project for
21962247
*/
2197-
private reloadConfiguredProjectForFiles<T>(openFiles: Map<T>, delayReload: boolean, shouldReloadProjectFor: (openFileValue: T) => boolean) {
2248+
private reloadConfiguredProjectForFiles<T>(openFiles: Map<T>, delayReload: boolean, shouldReloadProjectFor: (openFileValue: T) => boolean, reason: string) {
21982249
const updatedProjects = createMap<true>();
21992250
// try to reload config file for all open files
22002251
openFiles.forEach((openFileValue, path) => {
@@ -2215,11 +2266,12 @@ namespace ts.server {
22152266
if (!updatedProjects.has(configFileName)) {
22162267
if (delayReload) {
22172268
project.pendingReload = ConfigFileProgramReloadLevel.Full;
2269+
project.pendingReloadReason = reason;
22182270
this.delayUpdateProjectGraph(project);
22192271
}
22202272
else {
22212273
// reload from the disk
2222-
this.reloadConfiguredProject(project);
2274+
this.reloadConfiguredProject(project, reason);
22232275
}
22242276
updatedProjects.set(configFileName, true);
22252277
}
@@ -2306,7 +2358,8 @@ namespace ts.server {
23062358
const configFileName = this.getConfigFileNameForFile(originalFileInfo);
23072359
if (!configFileName) return undefined;
23082360

2309-
const configuredProject = this.findConfiguredProjectByProjectName(configFileName) || this.createAndLoadConfiguredProject(configFileName);
2361+
const configuredProject = this.findConfiguredProjectByProjectName(configFileName) ||
2362+
this.createAndLoadConfiguredProject(configFileName, `Creating project for original file: ${originalFileInfo.fileName} for location: ${location.fileName}`);
23102363
updateProjectIfDirty(configuredProject);
23112364
// Keep this configured project as referenced from project
23122365
addOriginalConfiguredProject(configuredProject);
@@ -2356,7 +2409,7 @@ namespace ts.server {
23562409
if (configFileName) {
23572410
project = this.findConfiguredProjectByProjectName(configFileName);
23582411
if (!project) {
2359-
project = this.createLoadAndUpdateConfiguredProject(configFileName);
2412+
project = this.createLoadAndUpdateConfiguredProject(configFileName, `Creating possible configured project for ${fileName} to open`);
23602413
// Send the event only if the project got created as part of this open request and info is part of the project
23612414
if (info.isOrphan()) {
23622415
// Since the file isnt part of configured project, do not send config file info
@@ -2797,8 +2850,8 @@ namespace ts.server {
27972850
if (!project) {
27982851
// errors are stored in the project, do not need to update the graph
27992852
project = this.getHostPreferences().lazyConfiguredProjectsFromExternalProject ?
2800-
this.createConfiguredProjectWithDelayLoad(tsconfigFile) :
2801-
this.createLoadAndUpdateConfiguredProject(tsconfigFile);
2853+
this.createConfiguredProjectWithDelayLoad(tsconfigFile, `Creating configured project in external project: ${proj.projectFileName}`) :
2854+
this.createLoadAndUpdateConfiguredProject(tsconfigFile, `Creating configured project in external project: ${proj.projectFileName}`);
28022855
}
28032856
if (project && !contains(exisingConfigFiles, tsconfigFile)) {
28042857
// keep project alive even if no documents are opened - its lifetime is bound to the lifetime of containing external project

src/server/project.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1320,6 +1320,8 @@ namespace ts.server {
13201320

13211321
/* @internal */
13221322
pendingReload: ConfigFileProgramReloadLevel;
1323+
/* @internal */
1324+
pendingReloadReason: string | undefined;
13231325

13241326
/*@internal*/
13251327
configFileSpecs: ConfigFileSpecs | undefined;
@@ -1339,6 +1341,9 @@ namespace ts.server {
13391341

13401342
protected isInitialLoadPending: () => boolean = returnTrue;
13411343

1344+
/*@internal*/
1345+
sendLoadingProjectFinish = false;
1346+
13421347
/*@internal*/
13431348
constructor(configFileName: NormalizedPath,
13441349
projectService: ProjectService,
@@ -1371,12 +1376,15 @@ namespace ts.server {
13711376
result = this.projectService.reloadFileNamesOfConfiguredProject(this);
13721377
break;
13731378
case ConfigFileProgramReloadLevel.Full:
1374-
this.projectService.reloadConfiguredProject(this);
1379+
const reason = Debug.assertDefined(this.pendingReloadReason);
1380+
this.pendingReloadReason = undefined;
1381+
this.projectService.reloadConfiguredProject(this, reason);
13751382
result = true;
13761383
break;
13771384
default:
13781385
result = super.updateGraph();
13791386
}
1387+
this.projectService.sendProjectLoadingFinishEvent(this);
13801388
this.projectService.sendProjectTelemetry(this);
13811389
this.projectService.sendSurveyReady(this);
13821390
return result;

src/server/protocol.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2452,6 +2452,30 @@ namespace ts.server.protocol {
24522452
openFiles: string[];
24532453
}
24542454

2455+
export type ProjectLoadingStartEventName = "projectLoadingStart";
2456+
export interface ProjectLoadingStartEvent extends Event {
2457+
event: ProjectLoadingStartEventName;
2458+
body: ProjectLoadingStartEventBody;
2459+
}
2460+
2461+
export interface ProjectLoadingStartEventBody {
2462+
/** name of the project */
2463+
projectName: string;
2464+
/** reason for loading */
2465+
reason: string;
2466+
}
2467+
2468+
export type ProjectLoadingFinishEventName = "projectLoadingFinish";
2469+
export interface ProjectLoadingFinishEvent extends Event {
2470+
event: ProjectLoadingFinishEventName;
2471+
body: ProjectLoadingFinishEventBody;
2472+
}
2473+
2474+
export interface ProjectLoadingFinishEventBody {
2475+
/** name of the project */
2476+
projectName: string;
2477+
}
2478+
24552479
export type SurveyReadyEventName = "surveyReady";
24562480

24572481
export interface SurveyReadyEvent extends Event {

src/server/session.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -568,9 +568,19 @@ namespace ts.server {
568568
const { openFiles } = event.data;
569569
this.projectsUpdatedInBackgroundEvent(openFiles);
570570
break;
571+
case ProjectLoadingStartEvent:
572+
const { project, reason } = event.data;
573+
this.event<protocol.ProjectLoadingStartEventBody>(
574+
{ projectName: project.getProjectName(), reason },
575+
ProjectLoadingStartEvent);
576+
break;
577+
case ProjectLoadingFinishEvent:
578+
const { project: finishProject } = event.data;
579+
this.event<protocol.ProjectLoadingFinishEventBody>({ projectName: finishProject.getProjectName() }, ProjectLoadingStartEvent);
580+
break;
571581
case LargeFileReferencedEvent:
572582
const { file, fileSize, maxFileSize } = event.data;
573-
this.event<protocol.LargeFileReferencedEventBody>({ file, fileSize, maxFileSize }, "largeFileReferenced");
583+
this.event<protocol.LargeFileReferencedEventBody>({ file, fileSize, maxFileSize }, LargeFileReferencedEvent);
574584
break;
575585
case ConfigFileDiagEvent:
576586
const { triggerFile, configFileName: configFile, diagnostics } = event.data;
@@ -579,14 +589,14 @@ namespace ts.server {
579589
triggerFile,
580590
configFile,
581591
diagnostics: bakedDiags
582-
}, "configFileDiag");
592+
}, ConfigFileDiagEvent);
583593
break;
584594
case SurveyReady:
585595
const { surveyId } = event.data;
586-
this.event<protocol.SurveyReadyEventBody>({ surveyId }, "surveyReady");
596+
this.event<protocol.SurveyReadyEventBody>({ surveyId }, SurveyReady);
587597
break;
588598
case ProjectLanguageServiceStateEvent: {
589-
const eventName: protocol.ProjectLanguageServiceStateEventName = "projectLanguageServiceState";
599+
const eventName: protocol.ProjectLanguageServiceStateEventName = ProjectLanguageServiceStateEvent;
590600
this.event<protocol.ProjectLanguageServiceStateEventBody>({
591601
projectName: event.data.project.getProjectName(),
592602
languageServiceEnabled: event.data.languageServiceEnabled
@@ -617,7 +627,7 @@ namespace ts.server {
617627
// Send project changed event
618628
this.event<protocol.ProjectsUpdatedInBackgroundEventBody>({
619629
openFiles
620-
}, "projectsUpdatedInBackground");
630+
}, ProjectsUpdatedInBackgroundEvent);
621631
}
622632
}
623633

0 commit comments

Comments
 (0)