@@ -29,7 +29,6 @@ internal abstract class LanguageServerProjectLoader
29
29
{
30
30
private readonly AsyncBatchingWorkQueue < ProjectToLoad > _projectsToReload ;
31
31
32
- protected readonly ProjectSystemProjectFactory ProjectFactory ;
33
32
private readonly ProjectTargetFrameworkManager _targetFrameworkManager ;
34
33
private readonly ProjectSystemHostInfo _projectSystemHostInfo ;
35
34
private readonly IFileChangeWatcher _fileChangeWatcher ;
@@ -70,7 +69,7 @@ private ProjectLoadState() { }
70
69
/// ID of the project which LSP uses to fulfill requests until the first design-time build is complete.
71
70
/// The project with this ID is removed from the workspace when unloading or when transitioning to <see cref="LoadedTargets"/> state.
72
71
/// </param>
73
- public sealed record Primordial ( ProjectId PrimordialProjectId ) : ProjectLoadState ;
72
+ public sealed record Primordial ( ProjectSystemProjectFactory Factory , ProjectId PrimordialProjectId ) : ProjectLoadState ;
74
73
75
74
/// <summary>
76
75
/// Represents a project for which we have loaded zero or more targets.
@@ -83,7 +82,6 @@ public sealed record LoadedTargets(ImmutableArray<LoadedProject> LoadedProjectTa
83
82
}
84
83
85
84
protected LanguageServerProjectLoader (
86
- ProjectSystemProjectFactory projectFactory ,
87
85
ProjectTargetFrameworkManager targetFrameworkManager ,
88
86
ProjectSystemHostInfo projectSystemHostInfo ,
89
87
IFileChangeWatcher fileChangeWatcher ,
@@ -94,7 +92,6 @@ protected LanguageServerProjectLoader(
94
92
ServerConfigurationFactory serverConfigurationFactory ,
95
93
IBinLogPathProvider binLogPathProvider )
96
94
{
97
- ProjectFactory = projectFactory ;
98
95
_targetFrameworkManager = targetFrameworkManager ;
99
96
_projectSystemHostInfo = projectSystemHostInfo ;
100
97
_fileChangeWatcher = fileChangeWatcher ;
@@ -103,7 +100,6 @@ protected LanguageServerProjectLoader(
103
100
_logger = loggerFactory . CreateLogger ( nameof ( LanguageServerProjectLoader ) ) ;
104
101
_projectLoadTelemetryReporter = projectLoadTelemetry ;
105
102
_binLogPathProvider = binLogPathProvider ;
106
- var workspace = projectFactory . Workspace ;
107
103
var razorDesignTimePath = serverConfigurationFactory . ServerConfiguration ? . RazorDesignTimePath ;
108
104
109
105
AdditionalProperties = razorDesignTimePath is null
@@ -174,7 +170,7 @@ private async ValueTask ReloadProjectsAsync(ImmutableSegmentedList<ProjectToLoad
174
170
}
175
171
}
176
172
177
- protected sealed record RemoteProjectLoadResult ( RemoteProjectFile ProjectFile , bool HasAllInformation , BuildHostProcessKind Preferred , BuildHostProcessKind Actual ) ;
173
+ protected sealed record RemoteProjectLoadResult ( RemoteProjectFile ProjectFile , ProjectSystemProjectFactory ProjectFactory , bool HasAllInformation , BuildHostProcessKind Preferred , BuildHostProcessKind Actual ) ;
178
174
179
175
/// <summary>Loads a project in the MSBuild host.</summary>
180
176
/// <remarks>Caller needs to catch exceptions to avoid bringing down the project loader queue.</remarks>
@@ -209,7 +205,7 @@ private async Task<bool> ReloadProjectAsync(ProjectToLoad projectToLoad, ToastEr
209
205
return false ;
210
206
}
211
207
212
- ( RemoteProjectFile remoteProjectFile , bool hasAllInformation , BuildHostProcessKind preferredBuildHostKind , BuildHostProcessKind actualBuildHostKind ) = remoteProjectLoadResult ;
208
+ ( RemoteProjectFile remoteProjectFile , ProjectSystemProjectFactory projectFactory , bool hasAllInformation , BuildHostProcessKind preferredBuildHostKind , BuildHostProcessKind actualBuildHostKind ) = remoteProjectLoadResult ;
213
209
if ( preferredBuildHostKind != actualBuildHostKind )
214
210
preferredBuildHostKindThatWeDidNotGet = preferredBuildHostKind ;
215
211
@@ -226,7 +222,7 @@ private async Task<bool> ReloadProjectAsync(ProjectToLoad projectToLoad, ToastEr
226
222
// The out-of-proc build host supports more languages than we may actually have Workspace binaries for, so ensure we can actually process that
227
223
// language in-process.
228
224
var projectLanguage = loadedProjectInfos . FirstOrDefault ( ) ? . Language ;
229
- if ( projectLanguage != null && ProjectFactory . Workspace . Services . GetLanguageService < ICommandLineParserService > ( projectLanguage ) == null )
225
+ if ( projectLanguage != null && projectFactory . Workspace . Services . GetLanguageService < ICommandLineParserService > ( projectLanguage ) == null )
230
226
{
231
227
return false ;
232
228
}
@@ -246,7 +242,7 @@ private async Task<bool> ReloadProjectAsync(ProjectToLoad projectToLoad, ToastEr
246
242
var newProjectTargetsBuilder = ArrayBuilder < LoadedProject > . GetInstance ( loadedProjectInfos . Length ) ;
247
243
foreach ( var loadedProjectInfo in loadedProjectInfos )
248
244
{
249
- var ( target , targetAlreadyExists ) = await GetOrCreateProjectTargetAsync ( previousProjectTargets , loadedProjectInfo ) ;
245
+ var ( target , targetAlreadyExists ) = await GetOrCreateProjectTargetAsync ( previousProjectTargets , projectFactory , loadedProjectInfo ) ;
250
246
newProjectTargetsBuilder . Add ( target ) ;
251
247
252
248
var ( targetTelemetryInfo , targetNeedsRestore ) = await target . UpdateWithNewProjectInfoAsync ( loadedProjectInfo , hasAllInformation , _logger ) ;
@@ -272,13 +268,13 @@ private async Task<bool> ReloadProjectAsync(ProjectToLoad projectToLoad, ToastEr
272
268
await _projectLoadTelemetryReporter . ReportProjectLoadTelemetryAsync ( telemetryInfos , projectToLoad , cancellationToken ) ;
273
269
}
274
270
275
- if ( currentLoadState is ProjectLoadState . Primordial ( var projectId ) )
271
+ if ( currentLoadState is ProjectLoadState . Primordial ( var primordialProjectFactory , var projectId ) )
276
272
{
277
273
// Remove the primordial project now that the design-time build pass is finished. This ensures that
278
274
// we have the new project in place before we remove the primordial project; otherwise for
279
275
// Miscellaneous Files we could have a case where we'd get another request to create a project
280
276
// for the project we're currently processing.
281
- await ProjectFactory . ApplyChangeToWorkspaceAsync ( workspace => workspace . OnProjectRemoved ( projectId ) , cancellationToken ) ;
277
+ await primordialProjectFactory . ApplyChangeToWorkspaceAsync ( workspace => workspace . OnProjectRemoved ( projectId ) , cancellationToken ) ;
282
278
}
283
279
284
280
_loadedProjects [ projectPath ] = new ProjectLoadState . LoadedTargets ( newProjectTargets ) ;
@@ -306,9 +302,9 @@ private async Task<bool> ReloadProjectAsync(ProjectToLoad projectToLoad, ToastEr
306
302
return false ;
307
303
}
308
304
309
- async Task < ( LoadedProject , bool alreadyExists ) > GetOrCreateProjectTargetAsync ( ImmutableArray < LoadedProject > previousProjectTargets , ProjectFileInfo loadedProjectInfo )
305
+ async Task < ( LoadedProject , bool alreadyExists ) > GetOrCreateProjectTargetAsync ( ImmutableArray < LoadedProject > previousProjectTargets , ProjectSystemProjectFactory projectFactory , ProjectFileInfo loadedProjectInfo )
310
306
{
311
- var existingProject = previousProjectTargets . FirstOrDefault ( p => p . GetTargetFramework ( ) == loadedProjectInfo . TargetFramework ) ;
307
+ var existingProject = previousProjectTargets . FirstOrDefault ( p => p . GetTargetFramework ( ) == loadedProjectInfo . TargetFramework && p . ProjectFactory == projectFactory ) ;
312
308
if ( existingProject != null )
313
309
{
314
310
return ( existingProject , alreadyExists : true ) ;
@@ -324,13 +320,13 @@ private async Task<bool> ReloadProjectAsync(ProjectToLoad projectToLoad, ToastEr
324
320
CompilationOutputAssemblyFilePath = loadedProjectInfo . IntermediateOutputFilePath ,
325
321
} ;
326
322
327
- var projectSystemProject = await ProjectFactory . CreateAndAddToWorkspaceAsync (
323
+ var projectSystemProject = await projectFactory . CreateAndAddToWorkspaceAsync (
328
324
projectSystemName ,
329
325
loadedProjectInfo . Language ,
330
326
projectCreationInfo ,
331
327
_projectSystemHostInfo ) ;
332
328
333
- var loadedProject = new LoadedProject ( projectSystemProject , ProjectFactory . Workspace . Services . SolutionServices , _fileChangeWatcher , _targetFrameworkManager ) ;
329
+ var loadedProject = new LoadedProject ( projectSystemProject , projectFactory , _fileChangeWatcher , _targetFrameworkManager ) ;
334
330
loadedProject . NeedsReload += ( _ , _ ) => _projectsToReload . AddWork ( projectToLoad with { ReportTelemetry = false } ) ;
335
331
return ( loadedProject , alreadyExists : false ) ;
336
332
}
@@ -358,14 +354,22 @@ async Task LogDiagnosticsAsync(ImmutableArray<DiagnosticLogItem> diagnosticLogIt
358
354
}
359
355
}
360
356
357
+ protected async ValueTask < bool > IsProjectLoadedAsync ( string projectPath , CancellationToken cancellationToken )
358
+ {
359
+ using ( await _gate . DisposableWaitAsync ( cancellationToken ) )
360
+ {
361
+ return _loadedProjects . ContainsKey ( projectPath ) ;
362
+ }
363
+ }
364
+
361
365
/// <summary>
362
366
/// Begins loading a project with an associated primordial project. Must not be called for a project which has already begun loading.
363
367
/// </summary>
364
368
/// <param name="doDesignTimeBuild">
365
369
/// If <see langword="true"/>, initiates a design-time build now, and starts file watchers to repeat the design-time build on relevant changes.
366
370
/// If <see langword="false"/>, only tracks the primordial project.
367
371
/// </param>
368
- protected async ValueTask BeginLoadingProjectWithPrimordialAsync ( string projectPath , ProjectId primordialProjectId , bool doDesignTimeBuild )
372
+ protected async ValueTask BeginLoadingProjectWithPrimordialAsync ( string projectPath , ProjectSystemProjectFactory primordialProjectFactory , ProjectId primordialProjectId , bool doDesignTimeBuild )
369
373
{
370
374
using ( await _gate . DisposableWaitAsync ( CancellationToken . None ) )
371
375
{
@@ -377,7 +381,7 @@ protected async ValueTask BeginLoadingProjectWithPrimordialAsync(string projectP
377
381
Contract . Fail ( $ "Cannot begin loading project '{ projectPath } ' because it has already begun loading.") ;
378
382
}
379
383
380
- _loadedProjects . Add ( projectPath , new ProjectLoadState . Primordial ( primordialProjectId ) ) ;
384
+ _loadedProjects . Add ( projectPath , new ProjectLoadState . Primordial ( primordialProjectFactory , primordialProjectId ) ) ;
381
385
if ( doDesignTimeBuild )
382
386
{
383
387
_projectsToReload . AddWork ( new ProjectToLoad ( projectPath , ProjectGuid : null , ReportTelemetry : true ) ) ;
@@ -416,9 +420,9 @@ protected async ValueTask UnloadProjectAsync(string projectPath)
416
420
return ;
417
421
}
418
422
419
- if ( loadState is ProjectLoadState . Primordial ( var projectId ) )
423
+ if ( loadState is ProjectLoadState . Primordial ( var projectFactory , var projectId ) )
420
424
{
421
- await ProjectFactory . ApplyChangeToWorkspaceAsync ( workspace => workspace . OnProjectRemoved ( projectId ) ) ;
425
+ await projectFactory . ApplyChangeToWorkspaceAsync ( workspace => workspace . OnProjectRemoved ( projectId ) ) ;
422
426
}
423
427
else if ( loadState is ProjectLoadState . LoadedTargets ( var existingProjects ) )
424
428
{
0 commit comments