Skip to content

Commit

Permalink
Merge pull request #66970 from mavasani/SymbolStartAnalyzers
Browse files Browse the repository at this point in the history
[Lightbulb Perf] Async lightbulb performance improvement
  • Loading branch information
mavasani authored Apr 3, 2023
2 parents 9c364c2 + be57910 commit 7be5509
Show file tree
Hide file tree
Showing 22 changed files with 675 additions and 132 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.Diagnostics;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions;

internal sealed class SuggestedActionPriorityProvider : ICodeActionRequestPriorityProvider
{
/// <summary>
/// Set of de-prioritized analyzers that were moved down from 'Normal' to 'Low'
/// priority bucket.
/// Note that this set is owned by the <see cref="SuggestedActionsSourceProvider.SuggestedActionsSource"/>
/// and shared across priority buckets.
/// </summary>
private readonly ConcurrentSet<DiagnosticAnalyzer> _lowPriorityAnalyzers;

public SuggestedActionPriorityProvider(CodeActionRequestPriority priority, ConcurrentSet<DiagnosticAnalyzer> lowPriorityAnalyzers)
{
Priority = priority;
_lowPriorityAnalyzers = lowPriorityAnalyzers;
}

public CodeActionRequestPriority Priority { get; }

public void AddDeprioritizedAnalyzerWithLowPriority(DiagnosticAnalyzer analyzer)
=> _lowPriorityAnalyzers.Add(analyzer);

public bool IsDeprioritizedAnalyzerWithLowPriority(DiagnosticAnalyzer analyzer)
=> _lowPriorityAnalyzers.Contains(analyzer);
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.Editor.Shared.Options;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
Expand Down Expand Up @@ -217,25 +218,28 @@ await InvokeBelowInputPriorityAsync(() =>
CodeActionOptionsProvider fallbackOptions,
CancellationToken cancellationToken)
{
var lowPriorityAnalyzers = new ConcurrentSet<DiagnosticAnalyzer>();

foreach (var order in Orderings)
{
var priority = TryGetPriority(order);
Contract.ThrowIfNull(priority);
var priorityProvider = new SuggestedActionPriorityProvider(priority.Value, lowPriorityAnalyzers);

var result = await GetFixLevelAsync(priority.Value).ConfigureAwait(false);
var result = await GetFixLevelAsync(priorityProvider).ConfigureAwait(false);
if (result != null)
return result;
}

return null;

async Task<string?> GetFixLevelAsync(CodeActionRequestPriority priority)
async Task<string?> GetFixLevelAsync(ICodeActionRequestPriorityProvider priorityProvider)
{
if (state.Target.Owner._codeFixService != null &&
state.Target.SubjectBuffer.SupportsCodeFixes())
{
var result = await state.Target.Owner._codeFixService.GetMostSevereFixAsync(
document, range.Span.ToTextSpan(), priority, fallbackOptions, cancellationToken).ConfigureAwait(false);
document, range.Span.ToTextSpan(), priorityProvider, fallbackOptions, cancellationToken).ConfigureAwait(false);

if (result.HasFix)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.Shared;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.Editor.Shared.Options;
Expand Down Expand Up @@ -103,6 +104,12 @@ private async Task GetSuggestedActionsWorkerAsync(

var pendingActionSets = new MultiDictionary<CodeActionRequestPriority, SuggestedActionSet>();

// Keep track of the diagnostic analyzers that have been deprioritized across calls to the
// diagnostic engine. We'll run them once we get around to the low-priority bucket. We want to
// keep track of this *across* calls to each priority. So we create this set outside of the loop and
// then pass it continuously from one priority group to the next.
var lowPriorityAnalyzers = new ConcurrentSet<DiagnosticAnalyzer>();

// Collectors are in priority order. So just walk them from highest to lowest.
foreach (var collector in collectors)
{
Expand All @@ -112,7 +119,7 @@ private async Task GetSuggestedActionsWorkerAsync(
state, requestedActionCategories, document,
range, selection,
addOperationScope: _ => null,
priority,
new SuggestedActionPriorityProvider(priority, lowPriorityAnalyzers),
currentActionCount, cancellationToken).WithCancellation(cancellationToken).ConfigureAwait(false);

await foreach (var set in allSets)
Expand Down Expand Up @@ -179,7 +186,7 @@ private async IAsyncEnumerable<SuggestedActionSet> GetCodeFixesAndRefactoringsAs
SnapshotSpan range,
TextSpan? selection,
Func<string, IDisposable?> addOperationScope,
CodeActionRequestPriority priority,
ICodeActionRequestPriorityProvider priorityProvider,
int currentActionCount,
[EnumeratorCancellation] CancellationToken cancellationToken)
{
Expand Down Expand Up @@ -218,7 +225,7 @@ Task<ImmutableArray<UnifiedSuggestedActionSet>> GetCodeFixesAsync()

return UnifiedSuggestedActionsSource.GetFilterAndOrderCodeFixesAsync(
workspace, owner._codeFixService, document, range.Span.ToTextSpan(),
priority, options, addOperationScope, cancellationToken).AsTask();
priorityProvider, options, addOperationScope, cancellationToken).AsTask();
}

Task<ImmutableArray<UnifiedSuggestedActionSet>> GetRefactoringsAsync()
Expand All @@ -239,15 +246,15 @@ Task<ImmutableArray<UnifiedSuggestedActionSet>> GetRefactoringsAsync()

// 'CodeActionRequestPriority.Lowest' is reserved for suppression/configuration code fixes.
// No code refactoring should have this request priority.
if (priority == CodeActionRequestPriority.Lowest)
if (priorityProvider.Priority == CodeActionRequestPriority.Lowest)
return SpecializedTasks.EmptyImmutableArray<UnifiedSuggestedActionSet>();

// If we are computing refactorings outside the 'Refactoring' context, i.e. for example, from the lightbulb under a squiggle or selection,
// then we want to filter out refactorings outside the selection span.
var filterOutsideSelection = !requestedActionCategories.Contains(PredefinedSuggestedActionCategoryNames.Refactoring);

return UnifiedSuggestedActionsSource.GetFilterAndOrderCodeRefactoringsAsync(
workspace, owner._codeRefactoringService, document, selection.Value, priority, options,
workspace, owner._codeRefactoringService, document, selection.Value, priorityProvider.Priority, options,
addOperationScope, filterOutsideSelection, cancellationToken);
}

Expand Down
Loading

0 comments on commit 7be5509

Please sign in to comment.