Skip to content

Commit

Permalink
Merge pull request dotnet#53117 from CyrusNajmabadi/asyncLightbulb
Browse files Browse the repository at this point in the history
Add support for async lightbulb model.
  • Loading branch information
CyrusNajmabadi authored Jun 29, 2021
2 parents 5336a84 + b7359c6 commit 0d317a4
Show file tree
Hide file tree
Showing 62 changed files with 705 additions and 129 deletions.
2 changes: 1 addition & 1 deletion eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
<MicrosoftCodeAnalysisTestingVersion>1.1.0-beta1.21322.2</MicrosoftCodeAnalysisTestingVersion>
<CodeStyleAnalyzerVersion>3.10.0</CodeStyleAnalyzerVersion>
<VisualStudioEditorPackagesVersion>16.10.230</VisualStudioEditorPackagesVersion>
<VisualStudioEditorNewPackagesVersion>17.0.65-g6c25c21ba5</VisualStudioEditorNewPackagesVersion>
<VisualStudioEditorNewPackagesVersion>17.0.83-preview</VisualStudioEditorNewPackagesVersion>
<ILAsmPackageVersion>5.0.0-alpha1.19409.1</ILAsmPackageVersion>
<ILDAsmPackageVersion>5.0.0-preview.1.20112.8</ILDAsmPackageVersion>
<MicrosoftVisualStudioLanguageServerProtocolPackagesVersion>17.0.20-g6553c6c46e</MicrosoftVisualStudioLanguageServerProtocolPackagesVersion>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System.Collections.Immutable;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.Diagnostics;

namespace Microsoft.CodeAnalysis.CodeStyle
Expand Down Expand Up @@ -44,6 +45,7 @@ protected AbstractBuiltInCodeStyleDiagnosticAnalyzer(ImmutableArray<DiagnosticDe
_localizableMessageFormat = Descriptor.MessageFormat;
}

public CodeActionRequestPriority RequestPriority => CodeActionRequestPriority.Normal;
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; }

protected static DiagnosticDescriptor CreateDescriptorWithId(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#nullable disable

using System.Collections.Immutable;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.Diagnostics;

Expand All @@ -28,6 +29,7 @@ protected AbstractCodeQualityDiagnosticAnalyzer(
_generatedCodeAnalysisFlags = generatedCodeAnalysisFlags;
}

public CodeActionRequestPriority RequestPriority => CodeActionRequestPriority.Normal;
public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; }

public sealed override void Initialize(AnalysisContext context)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
using Microsoft.CodeAnalysis.CodeActions;

#if CODE_STYLE
using OptionSet = Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptions;
Expand Down Expand Up @@ -117,6 +118,7 @@ public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
}
}

public CodeActionRequestPriority RequestPriority => CodeActionRequestPriority.Normal;
public bool OpenFileOnly(OptionSet options) => false;

public override void Initialize(AnalysisContext context)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Diagnostics;
Expand Down Expand Up @@ -184,6 +185,8 @@ private static void AccessSupportedDiagnostics(DiagnosticAnalyzer analyzer)

private class ThrowingDoNotCatchDiagnosticAnalyzer<TLanguageKindEnum> : ThrowingDiagnosticAnalyzer<TLanguageKindEnum>, IBuiltInAnalyzer where TLanguageKindEnum : struct
{
public CodeActionRequestPriority RequestPriority => CodeActionRequestPriority.Normal;

public bool OpenFileOnly(OptionSet options) => false;

public DiagnosticAnalyzerCategory GetAnalyzerCategory()
Expand Down
135 changes: 135 additions & 0 deletions src/EditorFeatures/Core.Wpf/Suggestions/AsyncSuggestedActionsSource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// 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 System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.Editor.Shared;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.UnifiedSuggestions;
using Microsoft.VisualStudio.Language.Intellisense;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions
{
internal partial class SuggestedActionsSourceProvider
{
private partial class AsyncSuggestedActionsSource : SuggestedActionsSource, ISuggestedActionsSourceExperimental
{
public AsyncSuggestedActionsSource(
IThreadingContext threadingContext,
SuggestedActionsSourceProvider owner,
ITextView textView,
ITextBuffer textBuffer,
ISuggestedActionCategoryRegistryService suggestedActionCategoryRegistry)
: base(threadingContext, owner, textView, textBuffer, suggestedActionCategoryRegistry)
{
}

public async IAsyncEnumerable<SuggestedActionSet> GetSuggestedActionsAsync(
ISuggestedActionCategorySet requestedActionCategories,
SnapshotSpan range,
[EnumeratorCancellation] CancellationToken cancellationToken)
{
AssertIsForeground();

using var state = SourceState.TryAddReference();
if (state is null)
yield break;

var workspace = state.Target.Workspace;
if (workspace is null)
yield break;

var selection = TryGetCodeRefactoringSelection(state, range);
await workspace.Services.GetRequiredService<IWorkspaceStatusService>().WaitUntilFullyLoadedAsync(cancellationToken).ConfigureAwait(false);

using (Logger.LogBlock(FunctionId.SuggestedActions_GetSuggestedActionsAsync, cancellationToken))
{
var document = range.Snapshot.GetOpenDocumentInCurrentContextWithChanges();
if (document is null)
yield break;

// Compute and return the high pri set of fixes and refactorings first so the user
// can act on them immediately without waiting on the regular set.
var highPriSet = GetCodeFixesAndRefactoringsAsync(
state, requestedActionCategories, document, range, selection, _ => null,
CodeActionRequestPriority.High, cancellationToken).WithCancellation(cancellationToken).ConfigureAwait(false);
await foreach (var set in highPriSet)
yield return set;

var lowPriSet = GetCodeFixesAndRefactoringsAsync(
state, requestedActionCategories, document, range, selection, _ => null,
CodeActionRequestPriority.Normal, cancellationToken).WithCancellation(cancellationToken).ConfigureAwait(false);
await foreach (var set in lowPriSet)
yield return set;
}
}

private async IAsyncEnumerable<SuggestedActionSet> GetCodeFixesAndRefactoringsAsync(
ReferenceCountedDisposable<State> state,
ISuggestedActionCategorySet requestedActionCategories,
Document document,
SnapshotSpan range,
TextSpan? selection,
Func<string, IDisposable?> addOperationScope,
CodeActionRequestPriority priority,
[EnumeratorCancellation] CancellationToken cancellationToken)
{
var workspace = document.Project.Solution.Workspace;
var supportsFeatureService = workspace.Services.GetRequiredService<ITextBufferSupportsFeatureService>();

var fixesTask = GetCodeFixesAsync(
state, supportsFeatureService, requestedActionCategories, workspace, document, range,
addOperationScope, priority, isBlocking: false, cancellationToken);
var refactoringsTask = GetRefactoringsAsync(
state, supportsFeatureService, requestedActionCategories, workspace, document, selection,
addOperationScope, priority, isBlocking: false, cancellationToken);

if (priority == CodeActionRequestPriority.High)
{
// in a high pri scenario, return data as soon as possible so that the user can interact with them.
// this is especially important for state-machine oriented refactorings (like rename) where the user
// should always have access to them effectively synchronously.
var firstTask = await Task.WhenAny(fixesTask, refactoringsTask).ConfigureAwait(false);
var secondTask = firstTask == fixesTask ? refactoringsTask : fixesTask;

var orderedTasks = new[] { firstTask, secondTask };
foreach (var task in orderedTasks)
{
if (task == fixesTask)
{
var fixes = await fixesTask.ConfigureAwait(false);
foreach (var set in ConvertToSuggestedActionSets(state, selection, fixes, ImmutableArray<UnifiedSuggestedActionSet>.Empty))
yield return set;
}
else
{
Contract.ThrowIfFalse(task == refactoringsTask);

var refactorings = await refactoringsTask.ConfigureAwait(false);
foreach (var set in ConvertToSuggestedActionSets(state, selection, ImmutableArray<UnifiedSuggestedActionSet>.Empty, refactorings))
yield return set;
}
}
}
else
{
var actionsArray = await Task.WhenAll(fixesTask, refactoringsTask).ConfigureAwait(false);
foreach (var set in ConvertToSuggestedActionSets(state, selection, fixes: actionsArray[0], refactorings: actionsArray[1]))
yield return set;
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ internal partial class SuggestedActionsSourceProvider
{
private partial class SuggestedActionsSource
{
private sealed class State : IDisposable
protected sealed class State : IDisposable
{
private readonly SuggestedActionsSource _source;

Expand Down
Loading

0 comments on commit 0d317a4

Please sign in to comment.