Skip to content

Commit

Permalink
CodeActionOptions
Browse files Browse the repository at this point in the history
  • Loading branch information
tmat committed Jan 10, 2022
1 parent 7f406fa commit 4d443fe
Show file tree
Hide file tree
Showing 24 changed files with 201 additions and 107 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,8 @@ void Method()
var diagnostics = await diagnosticService.GetDiagnosticsForSpanAsync(document, span);
Assert.Equal(2, diagnostics.Where(d => d.Id == "CS0219").Count());

var allFixes = (await fixService.GetFixesAsync(document, span, cancellationToken: CancellationToken.None))
var options = CodeActionOptions.Default;
var allFixes = (await fixService.GetFixesAsync(document, span, options, CancellationToken.None))
.SelectMany(fixCollection => fixCollection.Fixes);

var cs0219Fixes = allFixes.Where(fix => fix.PrimaryDiagnostic.Id == "CS0219").ToArray();
Expand Down
5 changes: 4 additions & 1 deletion src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.AddImports;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeCleanup;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
Expand Down Expand Up @@ -561,6 +562,8 @@ private protected static async Task AssertCodeCleanupResult(string expected, str
{
using var workspace = TestWorkspace.CreateCSharp(code, composition: EditorTestCompositions.EditorFeaturesWpf);

var options = CodeActionOptions.Default;

var solution = workspace.CurrentSolution
.WithOptions(workspace.Options
.WithChangedOption(GenerationOptions.PlaceSystemNamespaceFirst, LanguageNames.CSharp, systemUsingsFirst)
Expand All @@ -586,7 +589,7 @@ private protected static async Task AssertCodeCleanupResult(string expected, str
var enabledDiagnostics = codeCleanupService.GetAllDiagnostics();

var newDoc = await codeCleanupService.CleanupAsync(
document, enabledDiagnostics, new ProgressTracker(), CancellationToken.None);
document, enabledDiagnostics, new ProgressTracker(), options, CancellationToken.None);

var actual = await newDoc.GetTextAsync();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,12 +168,14 @@ private async IAsyncEnumerable<SuggestedActionSet> GetCodeFixesAndRefactoringsAs
var workspace = document.Project.Solution.Workspace;
var supportsFeatureService = workspace.Services.GetRequiredService<ITextBufferSupportsFeatureService>();

var options = CodeActionOptionsFactory.GetCodeActionOptions(document.Project, isBlocking: false);

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

await Task.WhenAll(fixesTask, refactoringsTask).ConfigureAwait(false);

Expand Down
22 changes: 14 additions & 8 deletions src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,15 +177,18 @@ public bool TryGetTelemetryId(out Guid telemetryId)
Func<string, IDisposable?> addOperationScope =
description => operationContext?.AddScope(allowCancellation: true, string.Format(EditorFeaturesResources.Gathering_Suggestions_0, description));

var options = CodeActionOptionsFactory.GetCodeActionOptions(document.Project, isBlocking: true);

// We convert the code fixes and refactorings to UnifiedSuggestedActionSets instead of
// SuggestedActionSets so that we can share logic between local Roslyn and LSP.
var fixesTask = GetCodeFixesAsync(
state, supportsFeatureService, requestedActionCategories, workspace, document, range,
addOperationScope, CodeActionRequestPriority.None,
isBlocking: true, cancellationToken);
options, cancellationToken);

var refactoringsTask = GetRefactoringsAsync(
state, supportsFeatureService, requestedActionCategories, GlobalOptions, workspace, document, selection,
addOperationScope, CodeActionRequestPriority.None, isBlocking: true, cancellationToken);
addOperationScope, CodeActionRequestPriority.None, options, cancellationToken);

Task.WhenAll(fixesTask, refactoringsTask).WaitAndGetResult(cancellationToken);

Expand Down Expand Up @@ -263,7 +266,7 @@ protected static Task<ImmutableArray<UnifiedSuggestedActionSet>> GetCodeFixesAsy
SnapshotSpan range,
Func<string, IDisposable?> addOperationScope,
CodeActionRequestPriority priority,
bool isBlocking,
CodeActionOptions options,
CancellationToken cancellationToken)
{
if (state.Target.Owner._codeFixService == null ||
Expand All @@ -275,7 +278,7 @@ protected static Task<ImmutableArray<UnifiedSuggestedActionSet>> GetCodeFixesAsy

return UnifiedSuggestedActionsSource.GetFilterAndOrderCodeFixesAsync(
workspace, state.Target.Owner._codeFixService, document, range.Span.ToTextSpan(),
priority, isBlocking, addOperationScope, cancellationToken).AsTask();
priority, options, addOperationScope, cancellationToken).AsTask();
}

private static string GetFixCategory(DiagnosticSeverity severity)
Expand Down Expand Up @@ -303,7 +306,7 @@ protected static Task<ImmutableArray<UnifiedSuggestedActionSet>> GetRefactorings
TextSpan? selection,
Func<string, IDisposable?> addOperationScope,
CodeActionRequestPriority priority,
bool isBlocking,
CodeActionOptions options,
CancellationToken cancellationToken)
{
if (!selection.HasValue)
Expand Down Expand Up @@ -332,7 +335,7 @@ protected static Task<ImmutableArray<UnifiedSuggestedActionSet>> GetRefactorings
var filterOutsideSelection = !requestedActionCategories.Contains(PredefinedSuggestedActionCategoryNames.Refactoring);

return UnifiedSuggestedActionsSource.GetFilterAndOrderCodeRefactoringsAsync(
workspace, state.Target.Owner._codeRefactoringService, document, selection.Value, priority, isBlocking,
workspace, state.Target.Owner._codeRefactoringService, document, selection.Value, priority, options,
addOperationScope, filterOutsideSelection, cancellationToken);
}

Expand Down Expand Up @@ -439,6 +442,7 @@ await InvokeBelowInputPriorityAsync(() =>
private async Task<string?> TryGetRefactoringSuggestedActionCategoryAsync(
Document document,
TextSpan? selection,
CodeActionOptions options,
CancellationToken cancellationToken)
{
using var state = _state.TryAddReference();
Expand All @@ -457,7 +461,7 @@ await InvokeBelowInputPriorityAsync(() =>
state.Target.SubjectBuffer.SupportsRefactorings())
{
if (await state.Target.Owner._codeRefactoringService.HasRefactoringsAsync(
document, selection.Value, cancellationToken).ConfigureAwait(false))
document, selection.Value, options, cancellationToken).ConfigureAwait(false))
{
return PredefinedSuggestedActionCategoryNames.Refactoring;
}
Expand Down Expand Up @@ -610,6 +614,8 @@ private void OnSuggestedActionsChanged(Workspace currentWorkspace, DocumentId? c
if (document == null)
return null;

var options = CodeActionOptionsFactory.GetCodeActionOptions(document.Project, isBlocking: true);

using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
var linkedToken = linkedTokenSource.Token;

Expand All @@ -621,7 +627,7 @@ private void OnSuggestedActionsChanged(Workspace currentWorkspace, DocumentId? c
if (selection != null)
{
refactoringTask = Task.Run(
() => TryGetRefactoringSuggestedActionCategoryAsync(document, selection, linkedToken), linkedToken);
() => TryGetRefactoringSuggestedActionCategoryAsync(document, selection, options, linkedToken), linkedToken);
}

// If we happen to get the result of the error task before the refactoring task,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// 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.SymbolSearch;

namespace Microsoft.CodeAnalysis.CodeActions
{
internal static class CodeActionOptionsFactory
{
internal static CodeActionOptions GetCodeActionOptions(Project project, bool isBlocking)
{
var options = project.Solution.Options;
var language = project.Language;

return new(
IsBlocking: isBlocking,
SearchReferenceAssemblies: options.GetOption(SymbolSearchOptions.SuggestForTypesInReferenceAssemblies, language),
HideAdvancedMembers: options.GetOption(CodeAnalysis.Completion.CompletionOptions.Metadata.HideAdvancedMembers, language));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -220,14 +220,15 @@ private static async ValueTask<ImmutableArray<UnifiedSuggestedActionSet>> GetAct
{
var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
var textSpan = ProtocolConversions.RangeToTextSpan(selection, text);
var options = CodeActionOptionsFactory.GetCodeActionOptions(document.Project, isBlocking: false);

var codeFixes = await UnifiedSuggestedActionsSource.GetFilterAndOrderCodeFixesAsync(
document.Project.Solution.Workspace, codeFixService, document, textSpan,
CodeActionRequestPriority.None,
isBlocking: false, addOperationScope: _ => null, cancellationToken).ConfigureAwait(false);
options, addOperationScope: _ => null, cancellationToken).ConfigureAwait(false);

var codeRefactorings = await UnifiedSuggestedActionsSource.GetFilterAndOrderCodeRefactoringsAsync(
document.Project.Solution.Workspace, codeRefactoringService, document, textSpan, CodeActionRequestPriority.None, isBlocking: false,
document.Project.Solution.Workspace, codeRefactoringService, document, textSpan, CodeActionRequestPriority.None, options,
addOperationScope: _ => null, filterOutsideSelection: false, cancellationToken).ConfigureAwait(false);

var actionSets = UnifiedSuggestedActionsSource.FilterAndOrderActionSets(codeFixes, codeRefactorings, textSpan, currentActionCount: 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ private static async Task<CodeRefactoring> GetCodeRefactoringAsync(
var span = documentsWithSelections.Single().SelectedSpans.Single();
var actions = ArrayBuilder<(CodeAction, TextSpan?)>.GetInstance();
var document = workspace.CurrentSolution.GetDocument(documentsWithSelections.Single().Id);
var context = new CodeRefactoringContext(document, span, (a, t) => actions.Add((a, t)), isBlocking: false, CancellationToken.None);
var options = CodeActionOptionsFactory.GetCodeActionOptions(document.Project, isBlocking: false);
var context = new CodeRefactoringContext(document, span, (a, t) => actions.Add((a, t)), options, CancellationToken.None);
await provider.ComputeRefactoringsAsync(context);

var result = actions.Count > 0 ? new CodeRefactoring(provider, actions.ToImmutable()) : null;
Expand Down
24 changes: 17 additions & 7 deletions src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ public async Task TestGetFixesAsyncWithDuplicateDiagnostics()
GetDocumentAndExtensionManager(tuple.analyzerService, workspace, out var document, out var extensionManager, analyzerReference);

// Verify that we do not crash when computing fixes.
_ = await tuple.codeFixService.GetFixesAsync(document, TextSpan.FromBounds(0, 0), cancellationToken: CancellationToken.None);
var options = CodeActionOptions.Default;

_ = await tuple.codeFixService.GetFixesAsync(document, TextSpan.FromBounds(0, 0), options, CancellationToken.None);

// Verify that code fix is invoked with both the diagnostics in the context,
// i.e. duplicate diagnostics are not silently discarded by the CodeFixService.
Expand All @@ -113,7 +115,8 @@ public async Task TestGetFixesAsyncHasNoDuplicateConfigurationActions()
GetDocumentAndExtensionManager(tuple.analyzerService, workspace, out var document, out var extensionManager, analyzerReference);

// Verify registered configuration code actions do not have duplicates.
var fixCollections = await tuple.codeFixService.GetFixesAsync(document, TextSpan.FromBounds(0, 0), cancellationToken: CancellationToken.None);
var options = CodeActionOptions.Default;
var fixCollections = await tuple.codeFixService.GetFixesAsync(document, TextSpan.FromBounds(0, 0), options, CancellationToken.None);
var codeActions = fixCollections.SelectMany(c => c.Fixes.Select(f => f.Action)).ToImmutableArray();
Assert.Equal(7, codeActions.Length);
var uniqueTitles = new HashSet<string>();
Expand Down Expand Up @@ -143,16 +146,18 @@ public async Task TestGetFixesAsyncForFixableAndNonFixableAnalyzersAsync()
using var workspace = tuple.workspace;
GetDocumentAndExtensionManager(tuple.analyzerService, workspace, out var document, out var extensionManager, analyzerReference);

var options = CodeActionOptions.Default;

// Verify only analyzerWithFix is executed when GetFixesAsync is invoked with 'CodeActionRequestPriority.Normal'.
_ = await tuple.codeFixService.GetFixesAsync(document, TextSpan.FromBounds(0, 0),
priority: CodeActionRequestPriority.Normal, isBlocking: false,
priority: CodeActionRequestPriority.Normal, options,
addOperationScope: _ => null, cancellationToken: CancellationToken.None);
Assert.True(analyzerWithFix.ReceivedCallback);
Assert.False(analyzerWithoutFix.ReceivedCallback);

// Verify both analyzerWithFix and analyzerWithoutFix are executed when GetFixesAsync is invoked with 'CodeActionRequestPriority.Lowest'.
_ = await tuple.codeFixService.GetFixesAsync(document, TextSpan.FromBounds(0, 0),
priority: CodeActionRequestPriority.Lowest, isBlocking: false,
priority: CodeActionRequestPriority.Lowest, options,
addOperationScope: _ => null, cancellationToken: CancellationToken.None);
Assert.True(analyzerWithFix.ReceivedCallback);
Assert.True(analyzerWithoutFix.ReceivedCallback);
Expand Down Expand Up @@ -180,8 +185,10 @@ public async Task TestGetFixesAsyncForDocumentDiagnosticAnalyzerAsync()
GetDocumentAndExtensionManager(tuple.analyzerService, workspace, out var document, out var extensionManager, analyzerReference);

// Verify both analyzers are executed when GetFixesAsync is invoked with 'CodeActionRequestPriority.Normal'.
var options = CodeActionOptions.Default;

_ = await tuple.codeFixService.GetFixesAsync(document, TextSpan.FromBounds(0, 0),
priority: CodeActionRequestPriority.Normal, isBlocking: false,
priority: CodeActionRequestPriority.Normal, options,
addOperationScope: _ => null, cancellationToken: CancellationToken.None);
Assert.True(documentDiagnosticAnalyzer.ReceivedCallback);
}
Expand Down Expand Up @@ -265,7 +272,8 @@ private static async Task<ImmutableArray<CodeFixCollection>> GetAddedFixesAsync(
var reference = new MockAnalyzerReference(codefix, ImmutableArray.Create(diagnosticAnalyzer));
var project = workspace.CurrentSolution.Projects.Single().AddAnalyzerReference(reference);
document = project.Documents.Single();
var fixes = await tuple.codeFixService.GetFixesAsync(document, TextSpan.FromBounds(0, 0), cancellationToken: CancellationToken.None);
var options = CodeActionOptions.Default;
var fixes = await tuple.codeFixService.GetFixesAsync(document, TextSpan.FromBounds(0, 0), options, CancellationToken.None);

if (exception)
{
Expand Down Expand Up @@ -679,7 +687,9 @@ private static async Task<ImmutableArray<CodeFixCollection>> GetNuGetAndVsixCode
var project = workspace.CurrentSolution.Projects.Single().AddAnalyzerReference(reference);

var document = project.Documents.Single();
return await fixService.GetFixesAsync(document, TextSpan.FromBounds(0, 0), cancellationToken: CancellationToken.None);
var options = CodeActionOptions.Default;

return await fixService.GetFixesAsync(document, TextSpan.FromBounds(0, 0), options, CancellationToken.None);
}

private sealed class NuGetCodeFixProvider : AbstractNuGetOrVsixCodeFixProvider
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ public async Task TestProjectRefactoringAsync()
var reference = new StubAnalyzerReference();
var project = workspace.CurrentSolution.Projects.Single().AddAnalyzerReference(reference);
var document = project.Documents.Single();
var refactorings = await refactoringService.GetRefactoringsAsync(document, TextSpan.FromBounds(0, 0), CancellationToken.None);
var options = CodeActionOptions.Default;
var refactorings = await refactoringService.GetRefactoringsAsync(document, TextSpan.FromBounds(0, 0), options, CancellationToken.None);

var stubRefactoringAction = refactorings.Single(refactoring => refactoring.CodeActions.FirstOrDefault().action?.Title == nameof(StubRefactoring));
Assert.True(stubRefactoringAction is object);
Expand All @@ -66,7 +67,8 @@ private static async Task VerifyRefactoringDisabledAsync<T>()
var project = workspace.CurrentSolution.Projects.Single();
var document = project.Documents.Single();
var extensionManager = (EditorLayerExtensionManager.ExtensionManager)document.Project.Solution.Workspace.Services.GetRequiredService<IExtensionManager>();
var result = await refactoringService.GetRefactoringsAsync(document, TextSpan.FromBounds(0, 0), CancellationToken.None);
var options = CodeActionOptions.Default;
var result = await refactoringService.GetRefactoringsAsync(document, TextSpan.FromBounds(0, 0), options, CancellationToken.None);
Assert.True(extensionManager.IsDisabled(codeRefactoring));
Assert.False(extensionManager.IsIgnored(codeRefactoring));

Expand Down
Loading

0 comments on commit 4d443fe

Please sign in to comment.