-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Extract local functions to make project updating simpler to understand. #74878
Changes from all commits
1fa8fcb
a2e1b9b
9fad9b8
645f85d
fb8abd2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -549,7 +549,8 @@ private async Task OnBatchScopeDisposedMaybeAsync(bool useAsync) | |
|
||
await _projectSystemProjectFactory.ApplyBatchChangeToWorkspaceMaybeAsync(useAsync, (solutionChanges, projectUpdateState) => | ||
{ | ||
// Changes made inside this transformation must be idemopotent in case it is attempted multiple times. | ||
// Changes made inside this transformation must be idempotent in case it is attempted multiple times. | ||
var projectBeforeMutations = solutionChanges.Solution.GetRequiredProject(Id); | ||
|
||
var documentFileNamesAddedBuilder = ImmutableArray.CreateBuilder<string>(); | ||
documentsToOpen = _sourceFiles.UpdateSolutionForBatch( | ||
|
@@ -578,92 +579,14 @@ await _projectSystemProjectFactory.ApplyBatchChangeToWorkspaceMaybeAsync(useAsyn | |
|
||
documentFileNamesAdded = documentFileNamesAddedBuilder.ToImmutable(); | ||
|
||
// Metadata reference removing. Do this before adding in case this removes a project reference that | ||
// we are also going to add in the same batch. This could happen if case is changing, or we're targeting | ||
// a different output path (say bin vs. obj vs. ref). | ||
foreach (var (path, properties) in _metadataReferencesRemovedInBatch) | ||
{ | ||
projectUpdateState = TryRemoveConvertedProjectReference_NoLock(Id, path, properties, projectUpdateState, out var projectReference); | ||
|
||
if (projectReference != null) | ||
{ | ||
solutionChanges.UpdateSolutionForProjectAction( | ||
Id, | ||
solutionChanges.Solution.RemoveProjectReference(Id, projectReference)); | ||
} | ||
else | ||
{ | ||
// TODO: find a cleaner way to fetch this | ||
var metadataReference = _projectSystemProjectFactory.Workspace.CurrentSolution.GetRequiredProject(Id).MetadataReferences | ||
.Cast<PortableExecutableReference>() | ||
.Single(m => m.FilePath == path && m.Properties == properties); | ||
|
||
projectUpdateState = projectUpdateState.WithIncrementalMetadataReferenceRemoved(metadataReference); | ||
|
||
solutionChanges.UpdateSolutionForProjectAction( | ||
Id, | ||
newSolution: solutionChanges.Solution.RemoveMetadataReference(Id, metadataReference)); | ||
} | ||
} | ||
projectUpdateState = UpdateMetadataReferences( | ||
projectBeforeMutations, solutionChanges, projectUpdateState, _metadataReferencesRemovedInBatch, _metadataReferencesAddedInBatch); | ||
|
||
// Metadata reference adding... | ||
if (_metadataReferencesAddedInBatch.Count > 0) | ||
{ | ||
var projectReferencesCreated = new List<ProjectReference>(); | ||
|
||
foreach (var (path, properties) in _metadataReferencesAddedInBatch) | ||
{ | ||
projectUpdateState = TryCreateConvertedProjectReference_NoLock( | ||
Id, path, properties, projectUpdateState, solutionChanges.Solution, out var projectReference); | ||
|
||
if (projectReference != null) | ||
{ | ||
projectReferencesCreated.Add(projectReference); | ||
} | ||
else | ||
{ | ||
var metadataReference = CreateMetadataReference_NoLock(path, properties, _projectSystemProjectFactory.SolutionServices); | ||
projectUpdateState = projectUpdateState.WithIncrementalMetadataReferenceAdded(metadataReference); | ||
} | ||
} | ||
UpdateProjectReferences( | ||
Id, solutionChanges, _projectReferencesRemovedInBatch, _projectReferencesAddedInBatch); | ||
|
||
solutionChanges.UpdateSolutionForProjectAction( | ||
Id, | ||
solutionChanges.Solution | ||
.AddProjectReferences(Id, projectReferencesCreated) | ||
.AddMetadataReferences(Id, projectUpdateState.AddedMetadataReferences)); | ||
} | ||
|
||
// Project reference adding... | ||
solutionChanges.UpdateSolutionForProjectAction( | ||
Id, | ||
newSolution: solutionChanges.Solution.AddProjectReferences(Id, _projectReferencesAddedInBatch)); | ||
|
||
// Project reference removing... | ||
foreach (var projectReference in _projectReferencesRemovedInBatch) | ||
{ | ||
solutionChanges.UpdateSolutionForProjectAction( | ||
Id, | ||
newSolution: solutionChanges.Solution.RemoveProjectReference(Id, projectReference)); | ||
} | ||
|
||
// Analyzer reference removing... | ||
if (_analyzersRemovedInBatch.Count > 0) | ||
{ | ||
projectUpdateState = projectUpdateState.WithIncrementalAnalyzerReferencesRemoved(_analyzersRemovedInBatch); | ||
|
||
foreach (var analyzerReference in _analyzersRemovedInBatch) | ||
solutionChanges.UpdateSolutionForProjectAction(Id, solutionChanges.Solution.RemoveAnalyzerReference(Id, analyzerReference)); | ||
} | ||
|
||
// Analyzer reference adding... | ||
if (_analyzersAddedInBatch.Count > 0) | ||
{ | ||
projectUpdateState = projectUpdateState.WithIncrementalAnalyzerReferencesAdded(_analyzersAddedInBatch); | ||
|
||
solutionChanges.UpdateSolutionForProjectAction( | ||
Id, solutionChanges.Solution.AddAnalyzerReferences(Id, _analyzersAddedInBatch)); | ||
} | ||
projectUpdateState = UpdateAnalyzerReferences( | ||
Id, solutionChanges, projectUpdateState, _analyzersRemovedInBatch, _analyzersAddedInBatch); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Three simple helpers now: Update metadata references. Update project references. Update analyzer references. |
||
|
||
// Other property modifications... | ||
foreach (var propertyModification in _projectPropertyModificationsInBatch) | ||
|
@@ -709,6 +632,117 @@ await _projectSystemProjectFactory.ApplyBatchChangeToWorkspaceMaybeAsync(useAsyn | |
if (hasAnalyzerChanges) | ||
_projectSystemProjectFactory.Workspace.EnqueueUpdateSourceGeneratorVersion(projectId: null, forceRegeneration: true); | ||
} | ||
|
||
static ProjectUpdateState UpdateMetadataReferences( | ||
Project projectBeforeMutation, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this guy is interesting in that we pass in the prior project state. that's needed as it is used to find teh MetadataRef instance associated with teh file path we want to remove. |
||
SolutionChangeAccumulator solutionChanges, | ||
ProjectUpdateState projectUpdateState, | ||
List<(string path, MetadataReferenceProperties properties)> metadataReferencesRemovedInBatch, | ||
List<(string path, MetadataReferenceProperties properties)> metadataReferencesAddedInBatch) | ||
{ | ||
var projectId = projectBeforeMutation.Id; | ||
|
||
// Metadata reference removing. Do this before adding in case this removes a project reference that we are also | ||
// going to add in the same batch. This could happen if case is changing, or we're targeting a different output | ||
// path (say bin vs. obj vs. ref). | ||
foreach (var (path, properties) in metadataReferencesRemovedInBatch) | ||
{ | ||
projectUpdateState = TryRemoveConvertedProjectReference_NoLock(projectId, path, properties, projectUpdateState, out var projectReference); | ||
|
||
if (projectReference != null) | ||
{ | ||
solutionChanges.UpdateSolutionForProjectAction( | ||
projectId, solutionChanges.Solution.RemoveProjectReference(projectId, projectReference)); | ||
} | ||
else | ||
{ | ||
var metadataReference = projectBeforeMutation.MetadataReferences | ||
.OfType<PortableExecutableReference>() | ||
.Single(m => m.FilePath == path && m.Properties == properties); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. note: this logic seems odd to me. doing a |
||
|
||
projectUpdateState = projectUpdateState.WithIncrementalMetadataReferenceRemoved(metadataReference); | ||
|
||
solutionChanges.UpdateSolutionForProjectAction( | ||
projectId, solutionChanges.Solution.RemoveMetadataReference(projectId, metadataReference)); | ||
} | ||
} | ||
|
||
// Metadata reference adding... | ||
if (metadataReferencesAddedInBatch.Count > 0) | ||
{ | ||
var projectReferencesCreated = new List<ProjectReference>(); | ||
|
||
foreach (var (path, properties) in metadataReferencesAddedInBatch) | ||
{ | ||
projectUpdateState = TryCreateConvertedProjectReference_NoLock( | ||
projectId, path, properties, projectUpdateState, solutionChanges.Solution, out var projectReference); | ||
|
||
if (projectReference != null) | ||
{ | ||
projectReferencesCreated.Add(projectReference); | ||
} | ||
else | ||
{ | ||
var metadataReference = CreateMetadataReference_NoLock(path, properties, solutionChanges.Solution.Services); | ||
projectUpdateState = projectUpdateState.WithIncrementalMetadataReferenceAdded(metadataReference); | ||
} | ||
} | ||
|
||
solutionChanges.UpdateSolutionForProjectAction( | ||
projectId, | ||
solutionChanges.Solution | ||
.AddProjectReferences(projectId, projectReferencesCreated) | ||
.AddMetadataReferences(projectId, projectUpdateState.AddedMetadataReferences)); | ||
} | ||
|
||
return projectUpdateState; | ||
} | ||
|
||
static void UpdateProjectReferences( | ||
ProjectId projectId, | ||
SolutionChangeAccumulator solutionChanges, | ||
List<ProjectReference> projectReferencesRemovedInBatch, | ||
List<ProjectReference> projectReferencesAddedInBatch) | ||
{ | ||
// Project reference adding... | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. note: project references swap the normal order. instead of remove/add, it's add/remove. I wasn't certain if it was safe to change that, so i kept it as is. |
||
solutionChanges.UpdateSolutionForProjectAction( | ||
projectId, solutionChanges.Solution.AddProjectReferences(projectId, projectReferencesAddedInBatch)); | ||
|
||
// Project reference removing... | ||
foreach (var projectReference in projectReferencesRemovedInBatch) | ||
{ | ||
solutionChanges.UpdateSolutionForProjectAction( | ||
projectId, solutionChanges.Solution.RemoveProjectReference(projectId, projectReference)); | ||
} | ||
} | ||
|
||
static ProjectUpdateState UpdateAnalyzerReferences( | ||
ProjectId projectId, | ||
SolutionChangeAccumulator solutionChanges, | ||
ProjectUpdateState projectUpdateState, | ||
List<AnalyzerFileReference> analyzersRemovedInBatch, | ||
List<AnalyzerFileReference> analyzersAddedInBatch) | ||
{ | ||
// Analyzer reference removing... | ||
if (analyzersRemovedInBatch.Count > 0) | ||
{ | ||
projectUpdateState = projectUpdateState.WithIncrementalAnalyzerReferencesRemoved(analyzersRemovedInBatch); | ||
|
||
foreach (var analyzerReference in analyzersRemovedInBatch) | ||
solutionChanges.UpdateSolutionForProjectAction(projectId, solutionChanges.Solution.RemoveAnalyzerReference(projectId, analyzerReference)); | ||
} | ||
|
||
// Analyzer reference adding... | ||
if (analyzersAddedInBatch.Count > 0) | ||
{ | ||
projectUpdateState = projectUpdateState.WithIncrementalAnalyzerReferencesAdded(analyzersAddedInBatch); | ||
|
||
solutionChanges.UpdateSolutionForProjectAction( | ||
projectId, solutionChanges.Solution.AddAnalyzerReferences(projectId, analyzersAddedInBatch)); | ||
} | ||
|
||
return projectUpdateState; | ||
} | ||
} | ||
|
||
#endregion | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the prior logic seems terrible/broken. We were accessing the mutable current soltuion of the workspace to make this decisino? That just seems never good. I changes to pass in the current 'immutable project state' that we're performing the mutation on as that seems sane and reasonavble.