diff --git a/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.cs b/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.cs index c2507355b196a992febf3f558d2abbdcc7409e39..bc21d2999cddcaf68fba886ba5326a4cde88efce 100644 --- a/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.cs +++ b/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.cs @@ -147,6 +147,9 @@ public async Task> GetFixesAsync(Document docu // // this design's weakness is that each side don't have enough information to narrow down works to do. it will most likely always do more works than needed. // sometimes way more than it is needed. (compilation) + + // group diagnostics by their diagnostics span + // invariant: later code gathers & runs CodeFixProviders for diagnostics with one identical diagnostics span (that get's later set as CodeFixCollection's TextSpan) Dictionary> aggregatedDiagnostics = null; foreach (var diagnostic in await _diagnosticService.GetDiagnosticsForSpanAsync(document, range, diagnosticIdOpt: null, includeConfigurationFixes, cancellationToken).ConfigureAwait(false)) { @@ -166,6 +169,7 @@ public async Task> GetFixesAsync(Document docu return ImmutableArray.Empty; } + // append fixes for all diagnostics with the same diagnostics span using var resultDisposer = ArrayBuilder.GetInstance(out var result); foreach (var spanAndDiagnostic in aggregatedDiagnostics) { @@ -257,6 +261,7 @@ public async Task ApplyCodeFixesForSpecificDiagnosticIdAsync(Document // TODO (https://github.com/dotnet/roslyn/issues/4932): Don't restrict CodeFixes in Interactive var isInteractive = document.Project.Solution.Workspace.Kind == WorkspaceKind.Interactive; + // gather CodeFixProviders for all distinct diagnostics found for current span foreach (var diagnosticId in diagnostics.Select(d => d.Id).Distinct()) { cancellationToken.ThrowIfCancellationRequested(); @@ -282,6 +287,7 @@ public async Task ApplyCodeFixesForSpecificDiagnosticIdAsync(Document var extensionManager = document.Project.Solution.Workspace.Services.GetService(); + // run each CodeFixProvider to gather individual CodeFixes for reported diagnostics foreach (var fixer in allFixers.Distinct()) { cancellationToken.ThrowIfCancellationRequested(); @@ -333,7 +339,7 @@ public async Task ApplyCodeFixesForSpecificDiagnosticIdAsync(Document } private async Task AppendConfigurationsAsync( - Document document, TextSpan span, IEnumerable diagnostics, + Document document, TextSpan diagnosticsSpan, IEnumerable diagnostics, ArrayBuilder result, CancellationToken cancellationToken) { if (!_configurationProvidersMap.TryGetValue(document.Project.Language, out var lazyConfigurationProviders) || lazyConfigurationProviders.Value == null) @@ -341,20 +347,21 @@ public async Task ApplyCodeFixesForSpecificDiagnosticIdAsync(Document return; } + // append CodeFixCollection for each CodeFixProvider foreach (var provider in lazyConfigurationProviders.Value) { await AppendFixesOrConfigurationsAsync( - document, span, diagnostics, fixAllForInSpan: false, result, provider, + document, diagnosticsSpan, diagnostics, fixAllForInSpan: false, result, provider, hasFix: d => provider.IsFixableDiagnostic(d), getFixes: dxs => provider.GetFixesAsync( - document, span, dxs, cancellationToken), + document, diagnosticsSpan, dxs, cancellationToken), cancellationToken: cancellationToken).ConfigureAwait(false); } } private async Task AppendFixesOrConfigurationsAsync( Document document, - TextSpan span, + TextSpan fixesSpan, IEnumerable diagnosticsWithSameSpan, bool fixAllForInSpan, ArrayBuilder result, @@ -413,7 +420,7 @@ await diagnosticsWithSameSpan.OrderByDescending(d => d.Severity) } var codeFix = new CodeFixCollection( - fixer, span, fixes, fixAllState, + fixer, fixesSpan, fixes, fixAllState, supportedScopes, diagnostics.First()); result.Add(codeFix); }