diff --git a/src/Analyzers/CSharp/CodeFixes/RemoveUnreachableCode/CSharpRemoveUnreachableCodeCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/RemoveUnreachableCode/CSharpRemoveUnreachableCodeCodeFixProvider.cs index 1d74eb4187264..43b9eebc3aac4 100644 --- a/src/Analyzers/CSharp/CodeFixes/RemoveUnreachableCode/CSharpRemoveUnreachableCodeCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/RemoveUnreachableCode/CSharpRemoveUnreachableCodeCodeFixProvider.cs @@ -40,7 +40,7 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context) // helpful, but shouldn't interfere with anything else the uesr is doing. var priority = IsSubsequentSection(diagnostic) ? CodeActionPriority.Low - : CodeActionPriority.Medium; + : CodeActionPriority.Default; RegisterCodeFix(context, CSharpCodeFixesResources.Remove_unreachable_code, nameof(CSharpCodeFixesResources.Remove_unreachable_code), priority); diff --git a/src/Analyzers/CSharp/CodeFixes/UseExpressionBody/UseExpressionBodyCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseExpressionBody/UseExpressionBodyCodeFixProvider.cs index 895587e7d83dd..16285cb3a0d70 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseExpressionBody/UseExpressionBodyCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseExpressionBody/UseExpressionBodyCodeFixProvider.cs @@ -44,7 +44,7 @@ public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) var priority = diagnostic.Severity == DiagnosticSeverity.Hidden ? CodeActionPriority.Low - : CodeActionPriority.Medium; + : CodeActionPriority.Default; var title = diagnostic.GetMessage(); diff --git a/src/Analyzers/Core/Analyzers/AbstractBuiltInCodeStyleDiagnosticAnalyzer_Core.cs b/src/Analyzers/Core/Analyzers/AbstractBuiltInCodeStyleDiagnosticAnalyzer_Core.cs index 37deb51455510..ccca032edc6fc 100644 --- a/src/Analyzers/Core/Analyzers/AbstractBuiltInCodeStyleDiagnosticAnalyzer_Core.cs +++ b/src/Analyzers/Core/Analyzers/AbstractBuiltInCodeStyleDiagnosticAnalyzer_Core.cs @@ -5,7 +5,6 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Linq; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Diagnostics; namespace Microsoft.CodeAnalysis.CodeStyle @@ -40,7 +39,7 @@ protected AbstractBuiltInCodeStyleDiagnosticAnalyzer(ImmutableArray descriptor.CustomTags.Any(t => t == WellKnownDiagnosticTags.Unnecessary)) || this is AbstractBuiltInUnnecessaryCodeStyleDiagnosticAnalyzer); } - public virtual CodeActionRequestPriority RequestPriority => CodeActionRequestPriority.Normal; + public virtual bool IsHighPriority => false; public sealed override ImmutableArray SupportedDiagnostics { get; } protected static DiagnosticDescriptor CreateDescriptorWithId( diff --git a/src/Analyzers/Core/Analyzers/AbstractCodeQualityDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/AbstractCodeQualityDiagnosticAnalyzer.cs index 19804a4a3c83f..8d38e131bd0fb 100644 --- a/src/Analyzers/Core/Analyzers/AbstractCodeQualityDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/AbstractCodeQualityDiagnosticAnalyzer.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Simplification; @@ -22,7 +21,7 @@ protected AbstractCodeQualityDiagnosticAnalyzer( _generatedCodeAnalysisFlags = generatedCodeAnalysisFlags; } - public CodeActionRequestPriority RequestPriority => CodeActionRequestPriority.Normal; + public bool IsHighPriority => false; public sealed override ImmutableArray SupportedDiagnostics { get; } public sealed override void Initialize(AnalysisContext context) diff --git a/src/Analyzers/Core/Analyzers/Formatting/AbstractFormattingAnalyzer.cs b/src/Analyzers/Core/Analyzers/Formatting/AbstractFormattingAnalyzer.cs index 6aef675eba03b..6af0a27abe3ce 100644 --- a/src/Analyzers/Core/Analyzers/Formatting/AbstractFormattingAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/Formatting/AbstractFormattingAnalyzer.cs @@ -2,8 +2,6 @@ // 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.Collections.Immutable; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting; @@ -35,7 +33,7 @@ protected sealed override void InitializeWorker(AnalysisContext context) /// them acting on an error reported in code, and can be computed fast as it only uses syntax not semantics. /// It's also the 8th most common fix that people use, and is picked almost all the times it is shown. /// - public override CodeActionRequestPriority RequestPriority => CodeActionRequestPriority.High; + public override bool IsHighPriority => true; private void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context) { diff --git a/src/Analyzers/Core/Analyzers/SimplifyTypeNames/SimplifyTypeNamesDiagnosticAnalyzerBase.cs b/src/Analyzers/Core/Analyzers/SimplifyTypeNames/SimplifyTypeNamesDiagnosticAnalyzerBase.cs index 7e31047299950..b8a55b69511ea 100644 --- a/src/Analyzers/Core/Analyzers/SimplifyTypeNames/SimplifyTypeNamesDiagnosticAnalyzerBase.cs +++ b/src/Analyzers/Core/Analyzers/SimplifyTypeNames/SimplifyTypeNamesDiagnosticAnalyzerBase.cs @@ -11,7 +11,6 @@ using System.Linq; using System.Runtime.CompilerServices; using System.Threading; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Options; diff --git a/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/AbstractAddAccessibilityModifiersCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/AbstractAddAccessibilityModifiersCodeFixProvider.cs index d98178ecf0b3e..ffe3a4049b3ed 100644 --- a/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/AbstractAddAccessibilityModifiersCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/AbstractAddAccessibilityModifiersCodeFixProvider.cs @@ -29,7 +29,7 @@ public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) var priority = diagnostic.Severity == DiagnosticSeverity.Hidden ? CodeActionPriority.Low - : CodeActionPriority.Medium; + : CodeActionPriority.Default; var (title, key) = diagnostic.Properties.ContainsKey(AddAccessibilityModifiersConstants.ModifiersAdded) ? (AnalyzersResources.Add_accessibility_modifiers, nameof(AnalyzersResources.Add_accessibility_modifiers)) diff --git a/src/Analyzers/Core/CodeFixes/ConflictMarkerResolution/AbstractConflictMarkerCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/ConflictMarkerResolution/AbstractConflictMarkerCodeFixProvider.cs index e4bc3872d2a84..9e8ec5825f43a 100644 --- a/src/Analyzers/Core/CodeFixes/ConflictMarkerResolution/AbstractConflictMarkerCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/ConflictMarkerResolution/AbstractConflictMarkerCodeFixProvider.cs @@ -45,12 +45,15 @@ protected AbstractResolveConflictMarkerCodeFixProvider( { FixableDiagnosticIds = ImmutableArray.Create(diagnosticId); _syntaxKinds = syntaxKinds; + +#if !CODE_STYLE + // Backdoor that allows this provider to use the high-priority bucket. + this.CustomTags = this.CustomTags.Add(CodeAction.CanBeHighPriorityTag); +#endif } public override ImmutableArray FixableDiagnosticIds { get; } -#if !CODE_STYLE - /// /// 'Fix merge conflict markers' gets special privileges. A core user scenario around them is that a user does /// a source control merge, gets conflicts, and then wants to open and edit them in the IDE very quickly. @@ -59,11 +62,9 @@ protected AbstractResolveConflictMarkerCodeFixProvider( /// them if they bring up the lightbulb on a <<<<<<< line, it should run ahead of /// normal fix providers else so the user can quickly fix the conflict and move onto the next conflict. /// - private protected override CodeActionRequestPriority ComputeRequestPriority() + protected override CodeActionRequestPriority ComputeRequestPriority() => CodeActionRequestPriority.High; -#endif - public override async Task RegisterCodeFixesAsync(CodeFixContext context) { var cancellationToken = context.CancellationToken; @@ -298,11 +299,14 @@ private static void RegisterCodeFixes( static CodeAction CreateCodeAction(string title, Func> action, string equivalenceKey) { -#if CODE_STYLE - return CodeAction.Create(title, action, equivalenceKey); -#else - return CodeAction.DocumentChangeAction.Create(title, action, equivalenceKey, CodeActionPriority.High); + var codeAction = CodeAction.Create(title, action, equivalenceKey, CodeActionPriority.High); + +#if !CODE_STYLE + // Backdoor that allows this provider to use the high-priority bucket. + codeAction.CustomTags = codeAction.CustomTags.Add(CodeAction.CanBeHighPriorityTag); #endif + + return codeAction; } } diff --git a/src/Analyzers/Core/CodeFixes/Formatting/FormattingCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/Formatting/FormattingCodeFixProvider.cs index 80c362ea00f0b..6203e8ad94130 100644 --- a/src/Analyzers/Core/CodeFixes/Formatting/FormattingCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/Formatting/FormattingCodeFixProvider.cs @@ -18,43 +18,43 @@ namespace Microsoft.CodeAnalysis.CodeStyle { internal abstract class AbstractFormattingCodeFixProvider : SyntaxEditorBasedCodeFixProvider { + protected AbstractFormattingCodeFixProvider() + { +#if !CODE_STYLE + // Backdoor that allows this provider to use the high-priority bucket. + this.CustomTags = this.CustomTags.Add(CodeAction.CanBeHighPriorityTag); +#endif + } + public sealed override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(IDEDiagnosticIds.FormattingDiagnosticId); protected abstract ISyntaxFormatting SyntaxFormatting { get; } -#if !CODE_STYLE - /// /// Fixing formatting is high priority. It's something the user wants to be able to fix quickly, is driven by /// them acting on an error reported in code, and can be computed fast as it only uses syntax not semantics. /// It's also the 8th most common fix that people use, and is picked almost all the times it is shown. /// - private protected override CodeActionRequestPriority ComputeRequestPriority() + protected override CodeActionRequestPriority ComputeRequestPriority() => CodeActionRequestPriority.High; -#endif - public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) { foreach (var diagnostic in context.Diagnostics) { -#if CODE_STYLE - context.RegisterCodeFix( - CodeAction.Create( - AnalyzersResources.Fix_formatting, - c => FixOneAsync(context, diagnostic, c), - nameof(AbstractFormattingCodeFixProvider)), - diagnostic); -#else - context.RegisterCodeFix( - CodeAction.DocumentChangeAction.Create( - AnalyzersResources.Fix_formatting, - c => FixOneAsync(context, diagnostic, c), - nameof(AbstractFormattingCodeFixProvider), - CodeActionPriority.High), - diagnostic); + var codeAction = CodeAction.Create( + AnalyzersResources.Fix_formatting, + c => FixOneAsync(context, diagnostic, c), + nameof(AbstractFormattingCodeFixProvider), + CodeActionPriority.High); + +#if !CODE_STYLE + // Backdoor that allows this provider to use the high-priority bucket. + codeAction.CustomTags = codeAction.CustomTags.Add(CodeAction.CanBeHighPriorityTag); #endif + + context.RegisterCodeFix(codeAction, diagnostic); } return Task.CompletedTask; diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs index 34160ae1f1787..b6ef1762a8f4b 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs @@ -186,7 +186,7 @@ private static void AccessSupportedDiagnostics(DiagnosticAnalyzer analyzer) private class ThrowingDoNotCatchDiagnosticAnalyzer : ThrowingDiagnosticAnalyzer, IBuiltInAnalyzer where TLanguageKindEnum : struct { - public CodeActionRequestPriority RequestPriority => CodeActionRequestPriority.Normal; + public bool IsHighPriority => false; public bool OpenFileOnly(SimplifierOptions? options) => false; diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionPriorityProvider.cs b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionPriorityProvider.cs index 9e3194ee53e52..130f2123ffd70 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionPriorityProvider.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionPriorityProvider.cs @@ -8,27 +8,20 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; -internal sealed class SuggestedActionPriorityProvider : ICodeActionRequestPriorityProvider +/// +/// Set of de-prioritized analyzers that were moved down from 'Normal' to 'Low' priority bucket. Note that this set is +/// owned by the and shared across priority buckets. +/// +internal sealed class SuggestedActionPriorityProvider( + CodeActionRequestPriority priority, + ConcurrentSet lowPriorityAnalyzers) + : ICodeActionRequestPriorityProvider { - /// - /// Set of de-prioritized analyzers that were moved down from 'Normal' to 'Low' - /// priority bucket. - /// Note that this set is owned by the - /// and shared across priority buckets. - /// - private readonly ConcurrentSet _lowPriorityAnalyzers; - - public SuggestedActionPriorityProvider(CodeActionRequestPriority priority, ConcurrentSet lowPriorityAnalyzers) - { - Priority = priority; - _lowPriorityAnalyzers = lowPriorityAnalyzers; - } - - public CodeActionRequestPriority Priority { get; } + public CodeActionRequestPriority? Priority { get; } = priority; public void AddDeprioritizedAnalyzerWithLowPriority(DiagnosticAnalyzer analyzer) - => _lowPriorityAnalyzers.Add(analyzer); + => lowPriorityAnalyzers.Add(analyzer); public bool IsDeprioritizedAnalyzerWithLowPriority(DiagnosticAnalyzer analyzer) - => _lowPriorityAnalyzers.Contains(analyzer); + => lowPriorityAnalyzers.Contains(analyzer); } diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/SuggestedAction.cs b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/SuggestedAction.cs index 6b2ff81043acd..f47573cd9b14f 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/SuggestedAction.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/SuggestedAction.cs @@ -65,8 +65,6 @@ internal SuggestedAction( CodeAction = codeAction; } - internal virtual CodeActionPriority Priority => CodeAction.Priority; - public virtual bool TryGetTelemetryId(out Guid telemetryId) { telemetryId = CodeAction.GetTelemetryId(); diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSourceProvider.cs b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSourceProvider.cs index 0baf85e77f689..d929eccea9019 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSourceProvider.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSourceProvider.cs @@ -105,7 +105,7 @@ public SuggestedActionsSourceProvider( => priority switch { DefaultOrderings.Highest => CodeActionRequestPriority.High, - DefaultOrderings.Default => CodeActionRequestPriority.Normal, + DefaultOrderings.Default => CodeActionRequestPriority.Default, DefaultOrderings.Low => CodeActionRequestPriority.Low, DefaultOrderings.Lowest => CodeActionRequestPriority.Lowest, _ => null, diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource_Async.cs b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource_Async.cs index c7e8bf0d93c7f..39602ba5ce6c6 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource_Async.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource_Async.cs @@ -137,7 +137,7 @@ private async Task GetSuggestedActionsWorkerAsync( { SuggestedActionSetPriority.None => CodeActionRequestPriority.Lowest, SuggestedActionSetPriority.Low => CodeActionRequestPriority.Low, - SuggestedActionSetPriority.Medium => CodeActionRequestPriority.Normal, + SuggestedActionSetPriority.Medium => CodeActionRequestPriority.Default, SuggestedActionSetPriority.High => CodeActionRequestPriority.High, _ => throw ExceptionUtilities.UnexpectedValue(set.Priority), }; @@ -233,7 +233,7 @@ private async IAsyncEnumerable GetCodeFixesAndRefactoringsAs async Task> GetCodeFixesAsync() { - using var _ = TelemetryLogging.LogBlockTimeAggregated(FunctionId.SuggestedAction_Summary, $"Total.Pri{(int)priorityProvider.Priority}.{nameof(GetCodeFixesAsync)}"); + using var _ = TelemetryLogging.LogBlockTimeAggregated(FunctionId.SuggestedAction_Summary, $"Total.Pri{priorityProvider.Priority.GetPriorityInt()}.{nameof(GetCodeFixesAsync)}"); if (owner._codeFixService == null || !supportsFeatureService.SupportsCodeFixes(target.SubjectBuffer) || @@ -249,7 +249,7 @@ async Task> GetCodeFixesAsync() async Task> GetRefactoringsAsync() { - using var _ = TelemetryLogging.LogBlockTimeAggregated(FunctionId.SuggestedAction_Summary, $"Total.Pri{(int)priorityProvider.Priority}.{nameof(GetRefactoringsAsync)}"); + using var _ = TelemetryLogging.LogBlockTimeAggregated(FunctionId.SuggestedAction_Summary, $"Total.Pri{priorityProvider.Priority.GetPriorityInt()}.{nameof(GetRefactoringsAsync)}"); if (!selection.HasValue) { @@ -325,7 +325,7 @@ static SuggestedActionSetPriority ConvertToSuggestedActionSetPriority(CodeAction { CodeActionPriority.Lowest => SuggestedActionSetPriority.None, CodeActionPriority.Low => SuggestedActionSetPriority.Low, - CodeActionPriority.Medium => SuggestedActionSetPriority.Medium, + CodeActionPriority.Default => SuggestedActionSetPriority.Medium, CodeActionPriority.High => SuggestedActionSetPriority.High, _ => throw ExceptionUtilities.Unreachable(), }; diff --git a/src/EditorFeatures/Core/EditAndContinue/EditAndContinueDiagnosticAnalyzer.cs b/src/EditorFeatures/Core/EditAndContinue/EditAndContinueDiagnosticAnalyzer.cs index a3202827cfbb0..3e9f2bd19dcd8 100644 --- a/src/EditorFeatures/Core/EditAndContinue/EditAndContinueDiagnosticAnalyzer.cs +++ b/src/EditorFeatures/Core/EditAndContinue/EditAndContinueDiagnosticAnalyzer.cs @@ -31,7 +31,7 @@ public override ImmutableArray SupportedDiagnostics public DiagnosticAnalyzerCategory GetAnalyzerCategory() => DiagnosticAnalyzerCategory.SemanticDocumentAnalysis; - public CodeActionRequestPriority RequestPriority => CodeActionRequestPriority.Normal; + public bool IsHighPriority => false; public bool OpenFileOnly(SimplifierOptions? options) => false; diff --git a/src/EditorFeatures/Core/RenameTracking/RenameTrackingCodeRefactoringProvider.cs b/src/EditorFeatures/Core/RenameTracking/RenameTrackingCodeRefactoringProvider.cs index dd5fb23f5958f..8e58131463e52 100644 --- a/src/EditorFeatures/Core/RenameTracking/RenameTrackingCodeRefactoringProvider.cs +++ b/src/EditorFeatures/Core/RenameTracking/RenameTrackingCodeRefactoringProvider.cs @@ -14,14 +14,23 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.RenameTracking { [ExportCodeRefactoringProvider(LanguageNames.CSharp, LanguageNames.VisualBasic, Name = PredefinedCodeRefactoringProviderNames.RenameTracking), Shared] - [method: ImportingConstructor] - [method: SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] - internal class RenameTrackingCodeRefactoringProvider( - ITextUndoHistoryRegistry undoHistoryRegistry, - [ImportMany] IEnumerable refactorNotifyServices) : CodeRefactoringProvider + internal class RenameTrackingCodeRefactoringProvider : CodeRefactoringProvider { - private readonly ITextUndoHistoryRegistry _undoHistoryRegistry = undoHistoryRegistry; - private readonly IEnumerable _refactorNotifyServices = refactorNotifyServices; + private readonly ITextUndoHistoryRegistry _undoHistoryRegistry; + private readonly IEnumerable _refactorNotifyServices; + + [ImportingConstructor] + [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] + public RenameTrackingCodeRefactoringProvider( + ITextUndoHistoryRegistry undoHistoryRegistry, + [ImportMany] IEnumerable refactorNotifyServices) + { + _undoHistoryRegistry = undoHistoryRegistry; + _refactorNotifyServices = refactorNotifyServices; + + // Backdoor that allows this provider to use the high-priority bucket. + this.CustomTags = this.CustomTags.Add(CodeAction.CanBeHighPriorityTag); + } public override Task ComputeRefactoringsAsync(CodeRefactoringContext context) { @@ -41,7 +50,7 @@ public override Task ComputeRefactoringsAsync(CodeRefactoringContext context) /// change the name of something and pop up the lightbulb without having to wait for the rest to /// compute. /// - private protected override CodeActionRequestPriority ComputeRequestPriority() + protected override CodeActionRequestPriority ComputeRequestPriority() => CodeActionRequestPriority.High; } } diff --git a/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.RenameTrackingCodeAction.cs b/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.RenameTrackingCodeAction.cs index 29acd00a67919..e4db8df1a4cc0 100644 --- a/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.RenameTrackingCodeAction.cs +++ b/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.RenameTrackingCodeAction.cs @@ -20,24 +20,39 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.RenameTracking { internal sealed partial class RenameTrackingTaggerProvider { - private class RenameTrackingCodeAction( - IThreadingContext threadingContext, - Document document, - string title, - IEnumerable refactorNotifyServices, - ITextUndoHistoryRegistry undoHistoryRegistry, - IGlobalOptionService globalOptions) : CodeAction + private class RenameTrackingCodeAction : CodeAction { - private readonly string _title = title; - private readonly IThreadingContext _threadingContext = threadingContext; - private readonly Document _document = document; - private readonly IEnumerable _refactorNotifyServices = refactorNotifyServices; - private readonly ITextUndoHistoryRegistry _undoHistoryRegistry = undoHistoryRegistry; - private readonly IGlobalOptionService _globalOptions = globalOptions; + private readonly string _title; + private readonly IThreadingContext _threadingContext; + private readonly Document _document; + private readonly IEnumerable _refactorNotifyServices; + private readonly ITextUndoHistoryRegistry _undoHistoryRegistry; + private readonly IGlobalOptionService _globalOptions; private RenameTrackingCommitter _renameTrackingCommitter; + public RenameTrackingCodeAction( + IThreadingContext threadingContext, + Document document, + string title, + IEnumerable refactorNotifyServices, + ITextUndoHistoryRegistry undoHistoryRegistry, + IGlobalOptionService globalOptions) + { + _threadingContext = threadingContext; + _document = document; + _title = title; + _refactorNotifyServices = refactorNotifyServices; + _undoHistoryRegistry = undoHistoryRegistry; + _globalOptions = globalOptions; + + // Backdoor that allows this provider to use the high-priority bucket. + this.CustomTags = this.CustomTags.Add(CodeAction.CanBeHighPriorityTag); + } + public override string Title => _title; - internal override CodeActionPriority Priority => CodeActionPriority.High; + + protected sealed override CodeActionPriority ComputePriority() + => CodeActionPriority.High; protected override Task> ComputeOperationsAsync(CancellationToken cancellationToken) { diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractSuppressionAllCodeTests.cs b/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractSuppressionAllCodeTests.cs index 9d8e5c02ee9e0..f02512c8096d7 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractSuppressionAllCodeTests.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractSuppressionAllCodeTests.cs @@ -144,7 +144,7 @@ internal class Analyzer : DiagnosticAnalyzer, IBuiltInAnalyzer private readonly DiagnosticDescriptor _descriptor = new DiagnosticDescriptor("TestId", "Test", "Test", "Test", DiagnosticSeverity.Warning, isEnabledByDefault: true); - public CodeActionRequestPriority RequestPriority => CodeActionRequestPriority.Normal; + public bool IsHighPriority => false; public bool OpenFileOnly(SimplifierOptions options) => false; diff --git a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs index 1492a820d9923..8a83b0e23099b 100644 --- a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs +++ b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs @@ -152,7 +152,7 @@ public async Task TestGetFixesAsyncForFixableAndNonFixableAnalyzersAsync() // Verify only analyzerWithFix is executed when GetFixesAsync is invoked with 'CodeActionRequestPriority.Normal'. _ = await tuple.codeFixService.GetFixesAsync(document, TextSpan.FromBounds(0, 0), - priorityProvider: new DefaultCodeActionRequestPriorityProvider(CodeActionRequestPriority.Normal), CodeActionOptions.DefaultProvider, + priorityProvider: new DefaultCodeActionRequestPriorityProvider(CodeActionRequestPriority.Default), CodeActionOptions.DefaultProvider, addOperationScope: _ => null, cancellationToken: CancellationToken.None); Assert.True(analyzerWithFix.ReceivedCallback); Assert.False(analyzerWithoutFix.ReceivedCallback); @@ -188,7 +188,7 @@ public async Task TestGetFixesAsyncForDocumentDiagnosticAnalyzerAsync() // Verify both analyzers are executed when GetFixesAsync is invoked with 'CodeActionRequestPriority.Normal'. _ = await tuple.codeFixService.GetFixesAsync(document, TextSpan.FromBounds(0, 0), - priorityProvider: new DefaultCodeActionRequestPriorityProvider(CodeActionRequestPriority.Normal), CodeActionOptions.DefaultProvider, + priorityProvider: new DefaultCodeActionRequestPriorityProvider(CodeActionRequestPriority.Default), CodeActionOptions.DefaultProvider, addOperationScope: _ => null, cancellationToken: CancellationToken.None); Assert.True(documentDiagnosticAnalyzer.ReceivedCallback); } @@ -218,7 +218,7 @@ public async Task TestGetFixesAsyncForGeneratorDiagnosticAsync() Assert.False(codeFix.Called); var fixCollectionSet = await tuple.codeFixService.GetFixesAsync(document, TextSpan.FromBounds(0, 0), - priorityProvider: new DefaultCodeActionRequestPriorityProvider(CodeActionRequestPriority.Normal), CodeActionOptions.DefaultProvider, + priorityProvider: new DefaultCodeActionRequestPriorityProvider(CodeActionRequestPriority.Default), CodeActionOptions.DefaultProvider, addOperationScope: _ => null, cancellationToken: CancellationToken.None); Assert.True(codeFix.Called); var fixCollection = Assert.Single(fixCollectionSet); @@ -1107,7 +1107,7 @@ void M() } var lowPriorityAnalyzers = new ConcurrentSet(); - var priorityProvider = new SuggestedActionPriorityProvider(CodeActionRequestPriority.Normal, lowPriorityAnalyzers); + var priorityProvider = new SuggestedActionPriorityProvider(CodeActionRequestPriority.Default, lowPriorityAnalyzers); var normalPriFixes = await tuple.codeFixService.GetFixesAsync(sourceDocument, testSpan, priorityProvider, CodeActionOptions.DefaultProvider, addOperationScope: _ => null, CancellationToken.None); priorityProvider = new SuggestedActionPriorityProvider(CodeActionRequestPriority.Low, lowPriorityAnalyzers); var lowPriFixes = await tuple.codeFixService.GetFixesAsync(sourceDocument, testSpan, priorityProvider, CodeActionOptions.DefaultProvider, addOperationScope: _ => null, CancellationToken.None); diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index 2cce6bb22ccde..c25881fa3ab02 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -1354,7 +1354,7 @@ public override void Initialize(AnalysisContext context) public DiagnosticAnalyzerCategory GetAnalyzerCategory() => DiagnosticAnalyzerCategory.SyntaxTreeWithoutSemanticsAnalysis; - public CodeActionRequestPriority RequestPriority => CodeActionRequestPriority.Normal; + public bool IsHighPriority => false; public bool OpenFileOnly(SimplifierOptions options) => true; diff --git a/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb b/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb index 6590ce7233722..4ceddc690a588 100644 --- a/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb @@ -2531,11 +2531,7 @@ class MyClass _category = category End Sub - Public ReadOnly Property RequestPriority As CodeActionRequestPriority Implements IBuiltInAnalyzer.RequestPriority - Get - Return CodeActionRequestPriority.Normal - End Get - End Property + Public ReadOnly Property IsHighPriority As Boolean Implements IBuiltInAnalyzer.IsHighPriority Public Function GetAnalyzerCategory() As DiagnosticAnalyzerCategory Implements IBuiltInAnalyzer.GetAnalyzerCategory Return _category diff --git a/src/Features/CSharp/Portable/ConvertBetweenRegularAndVerbatimString/AbstractConvertBetweenRegularAndVerbatimStringCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/ConvertBetweenRegularAndVerbatimString/AbstractConvertBetweenRegularAndVerbatimStringCodeRefactoringProvider.cs index 26e6cea3c14ac..5738b9f74e7a2 100644 --- a/src/Features/CSharp/Portable/ConvertBetweenRegularAndVerbatimString/AbstractConvertBetweenRegularAndVerbatimStringCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/ConvertBetweenRegularAndVerbatimString/AbstractConvertBetweenRegularAndVerbatimStringCodeRefactoringProvider.cs @@ -60,21 +60,21 @@ public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContex if (IsVerbatim(literalExpression)) { // always offer to convert from verbatim string to normal string. - context.RegisterRefactoring(CodeAction.CreateWithPriority( - CodeActionPriority.Low, + context.RegisterRefactoring(CodeAction.Create( CSharpFeaturesResources.Convert_to_regular_string, c => ConvertToRegularStringAsync(document, literalExpression, c), - nameof(CSharpFeaturesResources.Convert_to_regular_string))); + nameof(CSharpFeaturesResources.Convert_to_regular_string), + CodeActionPriority.Low)); } else if (ContainsSimpleEscape(charService, subStringTokens)) { // Offer to convert to a verbatim string if the normal string contains simple // escapes that can be directly embedded in the verbatim string. - context.RegisterRefactoring(CodeAction.CreateWithPriority( - CodeActionPriority.Low, + context.RegisterRefactoring(CodeAction.Create( CSharpFeaturesResources.Convert_to_verbatim_string, c => ConvertToVerbatimStringAsync(document, literalExpression, c), - nameof(CSharpFeaturesResources.Convert_to_verbatim_string))); + nameof(CSharpFeaturesResources.Convert_to_verbatim_string), + CodeActionPriority.Low)); } } diff --git a/src/Features/CSharp/Portable/ConvertProgram/ConvertToProgramMainCodeFixProvider.cs b/src/Features/CSharp/Portable/ConvertProgram/ConvertToProgramMainCodeFixProvider.cs index 7ded9a3a0b0ae..3baa5b364e119 100644 --- a/src/Features/CSharp/Portable/ConvertProgram/ConvertToProgramMainCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/ConvertProgram/ConvertToProgramMainCodeFixProvider.cs @@ -39,7 +39,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) var options = await document.GetCSharpCodeFixOptionsProviderAsync(context.Options, cancellationToken).ConfigureAwait(false); var priority = options.PreferTopLevelStatements.Notification.Severity == ReportDiagnostic.Hidden ? CodeActionPriority.Low - : CodeActionPriority.Medium; + : CodeActionPriority.Default; RegisterCodeFix(context, CSharpAnalyzersResources.Convert_to_Program_Main_style_program, nameof(ConvertToProgramMainCodeFixProvider), priority); } diff --git a/src/Features/CSharp/Portable/ConvertProgram/ConvertToProgramMainCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/ConvertProgram/ConvertToProgramMainCodeRefactoringProvider.cs index 7acbdd3dcbb1c..2054578938ce7 100644 --- a/src/Features/CSharp/Portable/ConvertProgram/ConvertToProgramMainCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/ConvertProgram/ConvertToProgramMainCodeRefactoringProvider.cs @@ -55,11 +55,11 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte if (!CanOfferUseProgramMain(options.PreferTopLevelStatements, root, compilation, forAnalyzer: false)) return; - context.RegisterRefactoring(CodeAction.CreateWithPriority( - CodeActionPriority.Low, + context.RegisterRefactoring(CodeAction.Create( CSharpAnalyzersResources.Convert_to_Program_Main_style_program, c => ConvertToProgramMainAsync(document, options.AccessibilityModifiersRequired.Value, c), - nameof(CSharpAnalyzersResources.Convert_to_Program_Main_style_program))); + nameof(CSharpAnalyzersResources.Convert_to_Program_Main_style_program), + CodeActionPriority.Low)); } } } diff --git a/src/Features/CSharp/Portable/ConvertProgram/ConvertToTopLevelStatementsCodeFixProvider.cs b/src/Features/CSharp/Portable/ConvertProgram/ConvertToTopLevelStatementsCodeFixProvider.cs index 6735aae2ba74d..31a0aacd1b600 100644 --- a/src/Features/CSharp/Portable/ConvertProgram/ConvertToTopLevelStatementsCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/ConvertProgram/ConvertToTopLevelStatementsCodeFixProvider.cs @@ -41,7 +41,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) var options = await document.GetCSharpCodeFixOptionsProviderAsync(context.Options, cancellationToken).ConfigureAwait(false); var priority = options.PreferTopLevelStatements.Notification.Severity == ReportDiagnostic.Hidden ? CodeActionPriority.Low - : CodeActionPriority.Medium; + : CodeActionPriority.Default; RegisterCodeFix(context, CSharpAnalyzersResources.Convert_to_top_level_statements, nameof(ConvertToTopLevelStatementsCodeFixProvider), priority); } diff --git a/src/Features/CSharp/Portable/ConvertProgram/ConvertToTopLevelStatementsCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/ConvertProgram/ConvertToTopLevelStatementsCodeRefactoringProvider.cs index 60f998d137dae..3ef8dc17b3256 100644 --- a/src/Features/CSharp/Portable/ConvertProgram/ConvertToTopLevelStatementsCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/ConvertProgram/ConvertToTopLevelStatementsCodeRefactoringProvider.cs @@ -58,11 +58,11 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte return; } - context.RegisterRefactoring(CodeAction.CreateWithPriority( - CodeActionPriority.Low, + context.RegisterRefactoring(CodeAction.Create( CSharpAnalyzersResources.Convert_to_top_level_statements, c => ConvertToTopLevelStatementsAsync(document, methodDeclaration, context.Options, c), - nameof(CSharpAnalyzersResources.Convert_to_top_level_statements))); + nameof(CSharpAnalyzersResources.Convert_to_top_level_statements), + CodeActionPriority.Low)); } } } diff --git a/src/Features/CSharp/Portable/ConvertToRawString/ConvertRegularStringToRawStringCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/ConvertToRawString/ConvertRegularStringToRawStringCodeRefactoringProvider.cs index 4336998893a53..4e5d11f832ed1 100644 --- a/src/Features/CSharp/Portable/ConvertToRawString/ConvertRegularStringToRawStringCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/ConvertToRawString/ConvertRegularStringToRawStringCodeRefactoringProvider.cs @@ -73,38 +73,38 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte // If we have escaped quotes in the string, then this is a good option to bubble up as something to convert // to a raw string. Otherwise, still offer this refactoring, but at low priority as the user may be // invoking this on lots of strings that they have no interest in converting. - var priority = AllEscapesAreQuotes(convertParams.Characters) ? CodeActionPriority.Medium : CodeActionPriority.Low; + var priority = AllEscapesAreQuotes(convertParams.Characters) ? CodeActionPriority.Default : CodeActionPriority.Low; var options = context.Options; if (convertParams.CanBeSingleLine) { context.RegisterRefactoring( - CodeAction.CreateWithPriority( - priority, + CodeAction.Create( CSharpFeaturesResources.Convert_to_raw_string, c => UpdateDocumentAsync(document, span, ConvertToRawKind.SingleLine, options, c), - s_kindToEquivalenceKeyMap[ConvertToRawKind.SingleLine]), + s_kindToEquivalenceKeyMap[ConvertToRawKind.SingleLine], + priority), token.Span); } else { context.RegisterRefactoring( - CodeAction.CreateWithPriority( - priority, + CodeAction.Create( CSharpFeaturesResources.Convert_to_raw_string, c => UpdateDocumentAsync(document, span, ConvertToRawKind.MultiLineIndented, options, c), - s_kindToEquivalenceKeyMap[ConvertToRawKind.MultiLineIndented]), + s_kindToEquivalenceKeyMap[ConvertToRawKind.MultiLineIndented], + priority), token.Span); if (convertParams.CanBeMultiLineWithoutLeadingWhiteSpaces) { context.RegisterRefactoring( - CodeAction.CreateWithPriority( - priority, + CodeAction.Create( CSharpFeaturesResources.without_leading_whitespace_may_change_semantics, c => UpdateDocumentAsync(document, span, ConvertToRawKind.MultiLineWithoutLeadingWhitespace, options, c), - s_kindToEquivalenceKeyMap[ConvertToRawKind.MultiLineWithoutLeadingWhitespace]), + s_kindToEquivalenceKeyMap[ConvertToRawKind.MultiLineWithoutLeadingWhitespace], + priority), token.Span); } } diff --git a/src/Features/CSharp/Portable/UsePatternMatching/CSharpIsAndCastCheckWithoutNameCodeFixProvider.cs b/src/Features/CSharp/Portable/UsePatternMatching/CSharpIsAndCastCheckWithoutNameCodeFixProvider.cs index 78c044ded4e59..bab73e8e2f33c 100644 --- a/src/Features/CSharp/Portable/UsePatternMatching/CSharpIsAndCastCheckWithoutNameCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UsePatternMatching/CSharpIsAndCastCheckWithoutNameCodeFixProvider.cs @@ -38,11 +38,11 @@ public override ImmutableArray FixableDiagnosticIds public override Task RegisterCodeFixesAsync(CodeFixContext context) { context.RegisterCodeFix( - CodeAction.CreateWithPriority( - CodeActionPriority.Low, + CodeAction.Create( CSharpAnalyzersResources.Use_pattern_matching, GetDocumentUpdater(context), - nameof(CSharpAnalyzersResources.Use_pattern_matching)), + nameof(CSharpAnalyzersResources.Use_pattern_matching), + CodeActionPriority.Low), context.Diagnostics); return Task.CompletedTask; } diff --git a/src/Features/Core/Portable/AddDebuggerDisplay/AbstractAddDebuggerDisplayCodeRefactoringProvider.cs b/src/Features/Core/Portable/AddDebuggerDisplay/AbstractAddDebuggerDisplayCodeRefactoringProvider.cs index 9a34c26ca8164..126ca8428c25e 100644 --- a/src/Features/Core/Portable/AddDebuggerDisplay/AbstractAddDebuggerDisplayCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/AddDebuggerDisplay/AbstractAddDebuggerDisplayCodeRefactoringProvider.cs @@ -48,7 +48,7 @@ await GetRelevantTypeFromHeaderAsync(context).ConfigureAwait(false) ?? if (debuggerAttributeTypeSymbol is null) return; - var typeSymbol = (INamedTypeSymbol)semanticModel.GetRequiredDeclaredSymbol(type, context.CancellationToken); + var typeSymbol = (INamedTypeSymbol)semanticModel.GetRequiredDeclaredSymbol(type, cancellationToken); if (typeSymbol.IsStatic || !IsClassOrStruct(typeSymbol)) return; @@ -56,11 +56,11 @@ await GetRelevantTypeFromHeaderAsync(context).ConfigureAwait(false) ?? if (HasDebuggerDisplayAttribute(typeSymbol, compilation)) return; - context.RegisterRefactoring(CodeAction.CreateWithPriority( - priority, + context.RegisterRefactoring(CodeAction.Create( FeaturesResources.Add_DebuggerDisplay_attribute, c => ApplyAsync(document, type, debuggerAttributeTypeSymbol, c), - nameof(FeaturesResources.Add_DebuggerDisplay_attribute))); + nameof(FeaturesResources.Add_DebuggerDisplay_attribute), + priority)); } private static async Task<(TTypeDeclarationSyntax type, CodeActionPriority priority)?> GetRelevantTypeFromHeaderAsync(CodeRefactoringContext context) @@ -92,7 +92,7 @@ await GetRelevantTypeFromHeaderAsync(context).ConfigureAwait(false) ?? if (typeDecl == null) return null; - var priority = isDebuggerDisplayMethod ? CodeActionPriority.Medium : CodeActionPriority.Low; + var priority = isDebuggerDisplayMethod ? CodeActionPriority.Default : CodeActionPriority.Low; return (typeDecl, priority); } diff --git a/src/Features/Core/Portable/AddImport/AbstractAddImportCodeFixProvider.cs b/src/Features/Core/Portable/AddImport/AbstractAddImportCodeFixProvider.cs index 0a1c7bdd01077..5382747026c82 100644 --- a/src/Features/Core/Portable/AddImport/AbstractAddImportCodeFixProvider.cs +++ b/src/Features/Core/Portable/AddImport/AbstractAddImportCodeFixProvider.cs @@ -29,6 +29,9 @@ protected AbstractAddImportCodeFixProvider( { _packageInstallerService = packageInstallerService; _symbolSearchService = symbolSearchService; + + // Backdoor that allows this provider to use the high-priority bucket. + this.CustomTags = this.CustomTags.Add(CodeAction.CanBeHighPriorityTag); } /// @@ -36,7 +39,7 @@ protected AbstractAddImportCodeFixProvider( /// 'smart tag' feature in VS prior to us even having 'light bulbs'. We want them to be computed /// first, ahead of everything else, and the main results should show up at the top of the list. /// - private protected override CodeActionRequestPriority ComputeRequestPriority() + protected override CodeActionRequestPriority ComputeRequestPriority() => CodeActionRequestPriority.High; public sealed override FixAllProvider? GetFixAllProvider() diff --git a/src/Features/Core/Portable/AddImport/CodeActions/AddImportCodeAction.cs b/src/Features/Core/Portable/AddImport/CodeActions/AddImportCodeAction.cs index b23387e8f845e..b4df04225d810 100644 --- a/src/Features/Core/Portable/AddImport/CodeActions/AddImportCodeAction.cs +++ b/src/Features/Core/Portable/AddImport/CodeActions/AddImportCodeAction.cs @@ -24,10 +24,10 @@ internal abstract partial class AbstractAddImportFeatureService Tags { get; } - internal sealed override CodeActionPriority Priority { get; } public sealed override string EquivalenceKey => Title; @@ -51,10 +51,16 @@ protected AddImportCodeAction( Title = fixData.Title; Tags = fixData.Tags.ToImmutableArrayOrEmpty().AddRange(additionalTags); - Priority = fixData.Priority; + _priority = fixData.Priority; _textChanges = fixData.TextChanges.ToImmutableArrayOrEmpty(); + + // Backdoor that allows this provider to use the high-priority bucket. + this.CustomTags = this.CustomTags.Add(CodeAction.CanBeHighPriorityTag); } + protected sealed override CodeActionPriority ComputePriority() + => _priority; + protected async Task GetUpdatedDocumentAsync(CancellationToken cancellationToken) { var oldText = await OriginalDocument.GetValueTextAsync(cancellationToken).ConfigureAwait(false); diff --git a/src/Features/Core/Portable/CodeFixes/AbstractConfigurationActionWithNestedActions.cs b/src/Features/Core/Portable/CodeFixes/AbstractConfigurationActionWithNestedActions.cs index e001a5f1212c5..d959f393d9585 100644 --- a/src/Features/Core/Portable/CodeFixes/AbstractConfigurationActionWithNestedActions.cs +++ b/src/Features/Core/Portable/CodeFixes/AbstractConfigurationActionWithNestedActions.cs @@ -25,7 +25,7 @@ protected AbstractConfigurationActionWithNestedActions(ImmutableArray - internal virtual CodeActionPriority AdditionalPriority => CodeActionPriority.Medium; + internal virtual CodeActionPriority AdditionalPriority => CodeActionPriority.Default; internal virtual bool IsBulkConfigurationAction => false; } diff --git a/src/Features/Core/Portable/CodeFixes/Suppression/NestedSuppressionCodeAction.cs b/src/Features/Core/Portable/CodeFixes/Suppression/NestedSuppressionCodeAction.cs index bb0c7d48e38f7..cfda1f27c2672 100644 --- a/src/Features/Core/Portable/CodeFixes/Suppression/NestedSuppressionCodeAction.cs +++ b/src/Features/Core/Portable/CodeFixes/Suppression/NestedSuppressionCodeAction.cs @@ -13,15 +13,16 @@ internal abstract class NestedSuppressionCodeAction : CodeAction protected NestedSuppressionCodeAction(string title) => Title = title; - // Put suppressions at the end of everything. - internal override CodeActionPriority Priority => CodeActionPriority.Lowest; - public sealed override string Title { get; } protected abstract string DiagnosticIdForEquivalenceKey { get; } public override string EquivalenceKey => Title + DiagnosticIdForEquivalenceKey; + // Put suppressions at the end of everything. + protected sealed override CodeActionPriority ComputePriority() + => CodeActionPriority.Lowest; + public static bool IsEquivalenceKeyForGlobalSuppression(string equivalenceKey) => equivalenceKey.StartsWith(FeaturesResources.in_Suppression_File); public static bool IsEquivalenceKeyForPragmaWarning(string equivalenceKey) diff --git a/src/Features/Core/Portable/CodeFixesAndRefactorings/CodeActionRequestPriorityProvider.cs b/src/Features/Core/Portable/CodeFixesAndRefactorings/CodeActionRequestPriorityProvider.cs index f44568d046c8e..a35f7b8032e5e 100644 --- a/src/Features/Core/Portable/CodeFixesAndRefactorings/CodeActionRequestPriorityProvider.cs +++ b/src/Features/Core/Portable/CodeFixesAndRefactorings/CodeActionRequestPriorityProvider.cs @@ -15,7 +15,10 @@ namespace Microsoft.CodeAnalysis.CodeActions { internal interface ICodeActionRequestPriorityProvider { - CodeActionRequestPriority Priority { get; } + /// + /// represents no specified priority. i.e. any priority should match this. + /// + CodeActionRequestPriority? Priority { get; } /// /// Tracks the given as a de-prioritized analyzer that should be moved to @@ -40,7 +43,7 @@ public static bool MatchesPriority(this ICodeActionRequestPriorityProvider provi var priority = provider.Priority; // If caller isn't asking for prioritized result, then run all analyzers. - if (priority == CodeActionRequestPriority.None) + if (priority is null) return true; // 'CodeActionRequestPriority.Lowest' is used for suppression/configuration fixes, @@ -63,10 +66,10 @@ public static bool MatchesPriority(this ICodeActionRequestPriorityProvider provi // Now compute this analyzer's priority and compare it with the provider's request 'Priority'. // Our internal 'IBuiltInAnalyzer' can specify custom request priority, while all - // the third-party analyzers are assigned 'Normal' priority. - var analyzerPriority = analyzer is IBuiltInAnalyzer { RequestPriority: var requestPriority } - ? requestPriority - : CodeActionRequestPriority.Normal; + // the third-party analyzers are assigned 'Medium' priority. + var analyzerPriority = analyzer is IBuiltInAnalyzer { IsHighPriority: true } + ? CodeActionRequestPriority.High + : CodeActionRequestPriority.Default; return priority == analyzerPriority; } @@ -77,11 +80,12 @@ public static bool MatchesPriority(this ICodeActionRequestPriorityProvider provi /// public static bool MatchesPriority(this ICodeActionRequestPriorityProvider provider, CodeFixProvider codeFixProvider) { - if (provider.Priority == CodeActionRequestPriority.None) + if (provider.Priority == null) { // We are computing fixes for all priorities return true; } + if (provider.Priority == CodeActionRequestPriority.Low) { // 'Low' priority can be used for two types of code fixers: @@ -97,12 +101,12 @@ public static bool MatchesPriority(this ICodeActionRequestPriorityProvider provi } } - internal sealed class DefaultCodeActionRequestPriorityProvider(CodeActionRequestPriority priority = CodeActionRequestPriority.None) : ICodeActionRequestPriorityProvider + internal sealed class DefaultCodeActionRequestPriorityProvider(CodeActionRequestPriority? priority = null) : ICodeActionRequestPriorityProvider { private readonly object _gate = new(); private HashSet? _lowPriorityAnalyzers; - public CodeActionRequestPriority Priority { get; } = priority; + public CodeActionRequestPriority? Priority { get; } = priority; public void AddDeprioritizedAnalyzerWithLowPriority(DiagnosticAnalyzer analyzer) { diff --git a/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs b/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs index bf2bfc582c38b..bb5fae6df9ed8 100644 --- a/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs +++ b/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs @@ -111,12 +111,12 @@ public async Task HasRefactoringsAsync( public async Task> GetRefactoringsAsync( TextDocument document, TextSpan state, - CodeActionRequestPriority priority, + CodeActionRequestPriority? priority, CodeActionOptionsProvider options, Func addOperationScope, CancellationToken cancellationToken) { - using (TelemetryLogging.LogBlockTimeAggregated(FunctionId.CodeRefactoring_Summary, $"Pri{(int)priority}")) + using (TelemetryLogging.LogBlockTimeAggregated(FunctionId.CodeRefactoring_Summary, $"Pri{priority.GetPriorityInt()}")) using (Logger.LogBlock(FunctionId.Refactoring_CodeRefactoringService_GetRefactoringsAsync, cancellationToken)) { var extensionManager = document.Project.Solution.Services.GetRequiredService(); @@ -124,28 +124,28 @@ public async Task> GetRefactoringsAsync( foreach (var provider in GetProviders(document)) { - if (priority != CodeActionRequestPriority.None && priority != provider.RequestPriority) + if (priority != null && priority != provider.RequestPriority) continue; tasks.Add(Task.Run(async () => + { + // Log an individual telemetry event for slow code refactoring computations to + // allow targeted trace notifications for further investigation. 500 ms seemed like + // a good value so as to not be too noisy, but if fired, indicates a potential + // area requiring investigation. + const int CodeRefactoringTelemetryDelay = 500; + + var providerName = provider.GetType().Name; + RefactoringToMetadataMap.TryGetValue(provider, out var providerMetadata); + + using (addOperationScope(providerName)) + using (RoslynEventSource.LogInformationalBlock(FunctionId.Refactoring_CodeRefactoringService_GetRefactoringsAsync, providerName, cancellationToken)) + using (TelemetryLogging.LogBlockTime(FunctionId.CodeRefactoring_Delay, $"{providerName}", CodeRefactoringTelemetryDelay)) { - // Log an individual telemetry event for slow code refactoring computations to - // allow targeted trace notifications for further investigation. 500 ms seemed like - // a good value so as to not be too noisy, but if fired, indicates a potential - // area requiring investigation. - const int CodeRefactoringTelemetryDelay = 500; - - var providerName = provider.GetType().Name; - RefactoringToMetadataMap.TryGetValue(provider, out var providerMetadata); - - using (addOperationScope(providerName)) - using (RoslynEventSource.LogInformationalBlock(FunctionId.Refactoring_CodeRefactoringService_GetRefactoringsAsync, providerName, cancellationToken)) - using (TelemetryLogging.LogBlockTime(FunctionId.CodeRefactoring_Delay, $"{providerName}", CodeRefactoringTelemetryDelay)) - { - return await GetRefactoringFromProviderAsync(document, state, provider, providerMetadata, - extensionManager, options, cancellationToken).ConfigureAwait(false); - } - }, + return await GetRefactoringFromProviderAsync(document, state, provider, providerMetadata, + extensionManager, options, cancellationToken).ConfigureAwait(false); + } + }, cancellationToken)); } diff --git a/src/Features/Core/Portable/CodeRefactorings/ICodeRefactoringService.cs b/src/Features/Core/Portable/CodeRefactorings/ICodeRefactoringService.cs index 3a38556eb2e98..aa425370bf7fb 100644 --- a/src/Features/Core/Portable/CodeRefactorings/ICodeRefactoringService.cs +++ b/src/Features/Core/Portable/CodeRefactorings/ICodeRefactoringService.cs @@ -15,12 +15,12 @@ internal interface ICodeRefactoringService { Task HasRefactoringsAsync(TextDocument document, TextSpan textSpan, CodeActionOptionsProvider options, CancellationToken cancellationToken); - Task> GetRefactoringsAsync(TextDocument document, TextSpan textSpan, CodeActionRequestPriority priority, CodeActionOptionsProvider options, Func addOperationScope, CancellationToken cancellationToken); + Task> GetRefactoringsAsync(TextDocument document, TextSpan textSpan, CodeActionRequestPriority? priority, CodeActionOptionsProvider options, Func addOperationScope, CancellationToken cancellationToken); } internal static class ICodeRefactoringServiceExtensions { public static Task> GetRefactoringsAsync(this ICodeRefactoringService service, TextDocument document, TextSpan state, CodeActionOptionsProvider options, CancellationToken cancellationToken) - => service.GetRefactoringsAsync(document, state, CodeActionRequestPriority.None, options, addOperationScope: _ => null, cancellationToken); + => service.GetRefactoringsAsync(document, state, priority: null, options, addOperationScope: _ => null, cancellationToken); } } diff --git a/src/Features/Core/Portable/ConvertToInterpolatedString/ConvertRegularStringToInterpolatedStringRefactoringProvider.cs b/src/Features/Core/Portable/ConvertToInterpolatedString/ConvertRegularStringToInterpolatedStringRefactoringProvider.cs index 662663933b8f6..a5ff998dadae2 100644 --- a/src/Features/Core/Portable/ConvertToInterpolatedString/ConvertRegularStringToInterpolatedStringRefactoringProvider.cs +++ b/src/Features/Core/Portable/ConvertToInterpolatedString/ConvertRegularStringToInterpolatedStringRefactoringProvider.cs @@ -73,11 +73,11 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte } context.RegisterRefactoring( - CodeAction.CreateWithPriority( - CodeActionPriority.Low, + CodeAction.Create( FeaturesResources.Convert_to_interpolated_string, _ => UpdateDocumentAsync(document, root, token), - nameof(FeaturesResources.Convert_to_interpolated_string)), + nameof(FeaturesResources.Convert_to_interpolated_string), + CodeActionPriority.Low), literalExpression.Span); } diff --git a/src/Features/Core/Portable/Diagnostics/Analyzers/UnboundIdentifiersDiagnosticAnalyzerBase.cs b/src/Features/Core/Portable/Diagnostics/Analyzers/UnboundIdentifiersDiagnosticAnalyzerBase.cs index 1629a901b796f..f75c2b33fd5b1 100644 --- a/src/Features/Core/Portable/Diagnostics/Analyzers/UnboundIdentifiersDiagnosticAnalyzerBase.cs +++ b/src/Features/Core/Portable/Diagnostics/Analyzers/UnboundIdentifiersDiagnosticAnalyzerBase.cs @@ -33,8 +33,8 @@ internal abstract class UnboundIdentifiersDiagnosticAnalyzerBase CodeActionRequestPriority.High; + public bool IsHighPriority + => true; public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptor); diff --git a/src/Features/Core/Portable/ExtractClass/ExtractClassWithDialogCodeAction.cs b/src/Features/Core/Portable/ExtractClass/ExtractClassWithDialogCodeAction.cs index a9ed17fcaddcc..a5c5f68b0b246 100644 --- a/src/Features/Core/Portable/ExtractClass/ExtractClassWithDialogCodeAction.cs +++ b/src/Features/Core/Portable/ExtractClass/ExtractClassWithDialogCodeAction.cs @@ -38,12 +38,18 @@ internal class ExtractClassWithDialogCodeAction( private readonly CleanCodeGenerationOptionsProvider _fallbackOptions = fallbackOptions; private readonly IExtractClassOptionsService _service = service; + // If the user brought up the lightbulb on a class itself, it's more likely that they want to extract a base + // class. on a member however, we deprioritize this as there are likely more member-specific operations + // they'd prefer to invoke instead. + private readonly CodeActionPriority _priority = selectedMembers.IsEmpty ? CodeActionPriority.Default : CodeActionPriority.Low; + public TextSpan Span { get; } = span; public override string Title => _selectedType.IsRecord ? FeaturesResources.Extract_base_record : FeaturesResources.Extract_base_class; - internal override CodeActionPriority Priority { get; } = selectedMembers.IsEmpty ? CodeActionPriority.Medium : CodeActionPriority.Low; + protected sealed override CodeActionPriority ComputePriority() + => _priority; public override object? GetOptions(CancellationToken cancellationToken) { diff --git a/src/Features/Core/Portable/MoveDeclarationNearReference/AbstractMoveDeclarationNearReferenceCodeRefactoringProvider.cs b/src/Features/Core/Portable/MoveDeclarationNearReference/AbstractMoveDeclarationNearReferenceCodeRefactoringProvider.cs index b09f15eb2a6cf..e534a95a74b82 100644 --- a/src/Features/Core/Portable/MoveDeclarationNearReference/AbstractMoveDeclarationNearReferenceCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/MoveDeclarationNearReference/AbstractMoveDeclarationNearReferenceCodeRefactoringProvider.cs @@ -43,11 +43,11 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte } context.RegisterRefactoring( - CodeAction.CreateWithPriority( - CodeActionPriority.Low, + CodeAction.Create( FeaturesResources.Move_declaration_near_reference, c => MoveDeclarationNearReferenceAsync(document, declaration, c), - nameof(FeaturesResources.Move_declaration_near_reference)), + nameof(FeaturesResources.Move_declaration_near_reference), + CodeActionPriority.Low), declaration.Span); } diff --git a/src/Features/Core/Portable/SpellCheck/AbstractSpellCheckCodeFixProvider.cs b/src/Features/Core/Portable/SpellCheck/AbstractSpellCheckCodeFixProvider.cs index 8e68d0016ffc6..1a02738e7e5ba 100644 --- a/src/Features/Core/Portable/SpellCheck/AbstractSpellCheckCodeFixProvider.cs +++ b/src/Features/Core/Portable/SpellCheck/AbstractSpellCheckCodeFixProvider.cs @@ -182,11 +182,11 @@ private async Task CheckItemsAsync( // Wrap the spell checking actions into a single top level suggestion // so as to not clutter the list. context.RegisterCodeFix( - CodeAction.CreateWithPriority( - CodeActionPriority.Low, + CodeAction.Create( string.Format(FeaturesResources.Fix_typo_0, nameText), codeActions, - isInlinable: true), + isInlinable: true, + CodeActionPriority.Low), context.Diagnostics); } else @@ -210,11 +210,11 @@ private static async Task GetInsertionTextAsync(Document document, Compl private CodeAction CreateCodeAction(SyntaxToken nameToken, string oldName, string newName, Document document) { - return CodeAction.CreateWithPriority( - CodeActionPriority.Low, + return CodeAction.Create( string.Format(FeaturesResources.Change_0_to_1, oldName, newName), c => UpdateAsync(document, nameToken, newName, c), - equivalenceKey: newName); + equivalenceKey: newName, + CodeActionPriority.Low); } private async Task UpdateAsync(Document document, SyntaxToken nameToken, string newName, CancellationToken cancellationToken) diff --git a/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs b/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs index cad221ec00cd3..e813a5931b8c9 100644 --- a/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs +++ b/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs @@ -53,12 +53,12 @@ public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) { var priority = diagnostic.Severity == DiagnosticSeverity.Hidden ? CodeActionPriority.Low - : CodeActionPriority.Medium; + : CodeActionPriority.Default; - context.RegisterCodeFix( - new UseAutoPropertyCodeAction( + context.RegisterCodeFix(CodeAction.SolutionChangeAction.Create( AnalyzersResources.Use_auto_property, c => ProcessResultAsync(context, diagnostic, c), + equivalenceKey: nameof(AnalyzersResources.Use_auto_property), priority), diagnostic); } @@ -343,24 +343,5 @@ private static bool IsWrittenToOutsideOfConstructorOrProperty( // We do need a setter return true; } - - private class UseAutoPropertyCodeAction : CustomCodeActions.SolutionChangeAction - { - public UseAutoPropertyCodeAction(string title, Func> createChangedSolution -#if !CODE_STYLE // 'CodeActionPriority' is not a public API, hence not supported in CodeStyle layer. - , CodeActionPriority priority -#endif - ) - : base(title, createChangedSolution, title) - { -#if !CODE_STYLE // 'CodeActionPriority' is not a public API, hence not supported in CodeStyle layer. - Priority = priority; -#endif - } - -#if !CODE_STYLE // 'CodeActionPriority' is not a public API, hence not supported in CodeStyle layer. - internal override CodeActionPriority Priority { get; } -#endif - } } } diff --git a/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs b/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs index a85c939575d78..d533662da97d1 100644 --- a/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs +++ b/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs @@ -82,7 +82,7 @@ public CodeFixService( // For CodeActionPriorityRequest.High, we only run compiler analyzer, which always has fixable diagnostics, // so we can return a null predicate here to include all diagnostics. - if (!(priorityProvider.Priority is CodeActionRequestPriority.Normal or CodeActionRequestPriority.Low)) + if (!(priorityProvider.Priority is CodeActionRequestPriority.Default or CodeActionRequestPriority.Low)) return null; var hasWorkspaceFixers = TryGetWorkspaceFixersMap(document, out var workspaceFixersMap); @@ -100,12 +100,12 @@ public CodeFixService( public async Task GetMostSevereFixAsync( TextDocument document, TextSpan range, ICodeActionRequestPriorityProvider priorityProvider, CodeActionOptionsProvider fallbackOptions, CancellationToken cancellationToken) { - using var _ = TelemetryLogging.LogBlockTimeAggregated(FunctionId.CodeFix_Summary, $"Pri{(int)priorityProvider.Priority}.{nameof(GetMostSevereFixAsync)}"); + using var _ = TelemetryLogging.LogBlockTimeAggregated(FunctionId.CodeFix_Summary, $"Pri{priorityProvider.Priority.GetPriorityInt()}.{nameof(GetMostSevereFixAsync)}"); ImmutableArray allDiagnostics; bool upToDate; - using (TelemetryLogging.LogBlockTimeAggregated(FunctionId.CodeFix_Summary, $"Pri{(int)priorityProvider.Priority}.{nameof(GetMostSevereFixAsync)}.{nameof(_diagnosticService.GetDiagnosticsForSpanAsync)}")) + using (TelemetryLogging.LogBlockTimeAggregated(FunctionId.CodeFix_Summary, $"Pri{priorityProvider.Priority.GetPriorityInt()}.{nameof(GetMostSevereFixAsync)}.{nameof(_diagnosticService.GetDiagnosticsForSpanAsync)}")) { (allDiagnostics, upToDate) = await _diagnosticService.TryGetDiagnosticsForSpanAsync( document, range, GetShouldIncludeDiagnosticPredicate(document, priorityProvider), @@ -171,11 +171,11 @@ public async IAsyncEnumerable StreamFixesAsync( Func addOperationScope, [EnumeratorCancellation] CancellationToken cancellationToken) { - using var _ = TelemetryLogging.LogBlockTimeAggregated(FunctionId.CodeFix_Summary, $"Pri{(int)priorityProvider.Priority}"); + using var _ = TelemetryLogging.LogBlockTimeAggregated(FunctionId.CodeFix_Summary, $"Pri{priorityProvider.Priority.GetPriorityInt()}"); // We only need to compute suppression/configuration fixes when request priority is - // 'CodeActionPriorityRequest.Lowest' or 'CodeActionPriorityRequest.None'. - var includeSuppressionFixes = priorityProvider.Priority is CodeActionRequestPriority.Lowest or CodeActionRequestPriority.None; + // 'CodeActionPriorityRequest.Lowest' or no priority was provided at all (so all providers should run). + var includeSuppressionFixes = priorityProvider.Priority is null or CodeActionRequestPriority.Lowest; // REVIEW: this is the first and simplest design. basically, when ctrl+. is pressed, it asks diagnostic // service to give back current diagnostics for the given span, and it will use that to get fixes. @@ -189,7 +189,7 @@ public async IAsyncEnumerable StreamFixesAsync( // user-invoked diagnostic requests, for example, user invoked Ctrl + Dot operation for lightbulb. ImmutableArray diagnostics; - using (TelemetryLogging.LogBlockTimeAggregated(FunctionId.CodeFix_Summary, $"Pri{(int)priorityProvider.Priority}.{nameof(_diagnosticService.GetDiagnosticsForSpanAsync)}")) + using (TelemetryLogging.LogBlockTimeAggregated(FunctionId.CodeFix_Summary, $"Pri{priorityProvider.Priority.GetPriorityInt()}.{nameof(_diagnosticService.GetDiagnosticsForSpanAsync)}")) { diagnostics = await _diagnosticService.GetDiagnosticsForSpanAsync( document, range, GetShouldIncludeDiagnosticPredicate(document, priorityProvider), diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs index 8e2cfaf1b5bea..f28d492712708 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs @@ -217,7 +217,7 @@ public async Task TryGetAsync(ArrayBuilder list, Cancellat using var _2 = ArrayBuilder.GetInstance(out var semanticSpanBasedAnalyzers); using var _3 = ArrayBuilder.GetInstance(out var semanticDocumentBasedAnalyzers); - using var _4 = TelemetryLogging.LogBlockTimeAggregated(FunctionId.RequestDiagnostics_Summary, $"Pri{(int)_priorityProvider.Priority}"); + using var _4 = TelemetryLogging.LogBlockTimeAggregated(FunctionId.RequestDiagnostics_Summary, $"Pri{_priorityProvider.Priority.GetPriorityInt()}"); foreach (var stateSet in _stateSets) { @@ -423,7 +423,7 @@ private async Task ComputeDocumentDiagnosticsAsync( ImmutableDictionary> diagnosticsMap; if (incrementalAnalysis) { - using var _2 = TelemetryLogging.LogBlockTimeAggregated(FunctionId.RequestDiagnostics_Summary, $"Pri{(int)_priorityProvider.Priority}.Incremental"); + using var _2 = TelemetryLogging.LogBlockTimeAggregated(FunctionId.RequestDiagnostics_Summary, $"Pri{_priorityProvider.Priority.GetPriorityInt()}.Incremental"); diagnosticsMap = await _owner._incrementalMemberEditAnalyzer.ComputeDiagnosticsAsync( executor, @@ -435,7 +435,7 @@ private async Task ComputeDocumentDiagnosticsAsync( } else { - using var _2 = TelemetryLogging.LogBlockTimeAggregated(FunctionId.RequestDiagnostics_Summary, $"Pri{(int)_priorityProvider.Priority}.Document"); + using var _2 = TelemetryLogging.LogBlockTimeAggregated(FunctionId.RequestDiagnostics_Summary, $"Pri{_priorityProvider.Priority.GetPriorityInt()}.Document"); diagnosticsMap = await ComputeDocumentDiagnosticsCoreAsync(executor, cancellationToken).ConfigureAwait(false); } @@ -470,7 +470,7 @@ async Task TryDeprioritizeAnalyzerAsync(DiagnosticAnalyzer analyzer, Docum // Conditions 1. and 2. if (kind != AnalysisKind.Semantic || !span.HasValue || - _priorityProvider.Priority != CodeActionRequestPriority.Normal) + _priorityProvider.Priority != CodeActionRequestPriority.Default) { return false; } diff --git a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs index 9d61bf4a90017..ca53549bcfe43 100644 --- a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs +++ b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs @@ -272,7 +272,7 @@ static CodeFixGroupKey GetGroupKey(CodeFix fix) /// /// Fix groups are returned in priority order determined based on . /// Priority for all s containing fixes is set to by default. The only exception is the case where a by default. The only exception is the case where a only contains suppression fixes - the priority of such s is set to so that suppression /// fixes always show up last after all other fixes (and refactorings) for the selected line of code. @@ -437,7 +437,7 @@ public static async Task> GetFilterAnd ICodeRefactoringService codeRefactoringService, TextDocument document, TextSpan selection, - CodeActionRequestPriority priority, + CodeActionRequestPriority? priority, CodeActionOptionsProvider options, Func addOperationScope, bool filterOutsideSelection, diff --git a/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionHelpers.cs b/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionHelpers.cs index 3f266525d0c86..cf28a49f1e9c5 100644 --- a/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionHelpers.cs +++ b/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionHelpers.cs @@ -322,7 +322,7 @@ private static async ValueTask> GetAct fallbackOptions, addOperationScope: _ => null, cancellationToken).ConfigureAwait(false); var codeRefactorings = await UnifiedSuggestedActionsSource.GetFilterAndOrderCodeRefactoringsAsync( - document.Project.Solution.Workspace, codeRefactoringService, document, textSpan, CodeActionRequestPriority.None, fallbackOptions, + document.Project.Solution.Workspace, codeRefactoringService, document, textSpan, priority: null, fallbackOptions, addOperationScope: _ => null, filterOutsideSelection: false, cancellationToken).ConfigureAwait(false); var actionSets = UnifiedSuggestedActionsSource.FilterAndOrderActionSets( @@ -345,7 +345,7 @@ private static CodeActionKind GetCodeActionKindFromSuggestedActionCategoryName(s { CodeActionPriority.Lowest => LSP.VSInternalPriorityLevel.Lowest, CodeActionPriority.Low => LSP.VSInternalPriorityLevel.Low, - CodeActionPriority.Medium => LSP.VSInternalPriorityLevel.Normal, + CodeActionPriority.Default => LSP.VSInternalPriorityLevel.Normal, CodeActionPriority.High => LSP.VSInternalPriorityLevel.High, _ => throw ExceptionUtilities.UnexpectedValue(priority) }; diff --git a/src/Features/VisualBasicTest/AddImport/AddImportTests.vb b/src/Features/VisualBasicTest/AddImport/AddImportTests.vb index 3ccae3b1008f3..0c9bc7dff928c 100644 --- a/src/Features/VisualBasicTest/AddImport/AddImportTests.vb +++ b/src/Features/VisualBasicTest/AddImport/AddImportTests.vb @@ -63,7 +63,7 @@ End Namespace Class Class1 Inherits TextBox -End Class", testHost, priority:=CodeActionPriority.Medium) +End Class", testHost, priority:=CodeActionPriority.Default) End Function diff --git a/src/Tools/ExternalAccess/FSharp/Internal/Diagnostics/FSharpDocumentDiagnosticAnalyzer.cs b/src/Tools/ExternalAccess/FSharp/Internal/Diagnostics/FSharpDocumentDiagnosticAnalyzer.cs index 54a698d91c142..e449119c8fd1c 100644 --- a/src/Tools/ExternalAccess/FSharp/Internal/Diagnostics/FSharpDocumentDiagnosticAnalyzer.cs +++ b/src/Tools/ExternalAccess/FSharp/Internal/Diagnostics/FSharpDocumentDiagnosticAnalyzer.cs @@ -64,7 +64,7 @@ public static ImmutableArray CreateSupportedDiagnostics() return dummyDescriptors.ToImmutable(); } - public CodeActionRequestPriority RequestPriority => CodeActionRequestPriority.Normal; + public bool IsHighPriority => false; public override int Priority => 10; // Default = 50 diff --git a/src/Tools/ExternalAccess/FSharp/Internal/Diagnostics/FSharpSimplifyNameDiagnosticAnalyzer.cs b/src/Tools/ExternalAccess/FSharp/Internal/Diagnostics/FSharpSimplifyNameDiagnosticAnalyzer.cs index 8dcc162607ea6..0a829bb6a7f19 100644 --- a/src/Tools/ExternalAccess/FSharp/Internal/Diagnostics/FSharpSimplifyNameDiagnosticAnalyzer.cs +++ b/src/Tools/ExternalAccess/FSharp/Internal/Diagnostics/FSharpSimplifyNameDiagnosticAnalyzer.cs @@ -50,7 +50,7 @@ internal class FSharpSimplifyNameDiagnosticAnalyzer : DocumentDiagnosticAnalyzer public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(_descriptor); - public CodeActionRequestPriority RequestPriority => CodeActionRequestPriority.Normal; + public bool IsHighPriority => false; public override int Priority => 100; // Default = 50 diff --git a/src/Tools/ExternalAccess/FSharp/Internal/Diagnostics/FSharpUnusedDeclarationsAnalyzer.cs b/src/Tools/ExternalAccess/FSharp/Internal/Diagnostics/FSharpUnusedDeclarationsAnalyzer.cs index 062e00ea993a9..187016406454b 100644 --- a/src/Tools/ExternalAccess/FSharp/Internal/Diagnostics/FSharpUnusedDeclarationsAnalyzer.cs +++ b/src/Tools/ExternalAccess/FSharp/Internal/Diagnostics/FSharpUnusedDeclarationsAnalyzer.cs @@ -52,7 +52,7 @@ internal class FSharpUnusedDeclarationsDiagnosticAnalyzer : DocumentDiagnosticAn public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(_descriptor); - public CodeActionRequestPriority RequestPriority => CodeActionRequestPriority.Normal; + public bool IsHighPriority => false; public override int Priority => 80; // Default = 50 diff --git a/src/Workspaces/Core/Portable/CodeActions/CodeAction.cs b/src/Workspaces/Core/Portable/CodeActions/CodeAction.cs index d0adc66addb38..afca88c6b2e51 100644 --- a/src/Workspaces/Core/Portable/CodeActions/CodeAction.cs +++ b/src/Workspaces/Core/Portable/CodeActions/CodeAction.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; @@ -34,6 +35,12 @@ namespace Microsoft.CodeAnalysis.CodeActions /// public abstract class CodeAction { + /// + /// Special tag that indicates that it's this is a privileged code action that is allowed to use the priority class. + /// + internal static readonly string CanBeHighPriorityTag = Guid.NewGuid().ToString(); + /// /// Tag we use to convey that this code action should only be shown if it's in a host that allows for /// non-document changes. For example if it needs to make project changes, or if will show host-specific UI. @@ -76,7 +83,40 @@ public abstract class CodeAction internal virtual bool IsInlinable => false; - internal virtual CodeActionPriority Priority => CodeActionPriority.Default; + /// + /// Priority of this particular action within a group of other actions. Less relevant actions should override + /// this and specify a lower priority so that more important actions are easily accessible to the user. Returns + /// if not overridden. + /// + public CodeActionPriority Priority + { + get + { + var priority = ComputePriority(); + if (priority < CodeActionPriority.Lowest) + priority = CodeActionPriority.Lowest; + + if (priority > CodeActionPriority.High) + priority = CodeActionPriority.High; + + if (priority == CodeActionPriority.High && !this.CustomTags.Contains(CanBeHighPriorityTag)) + priority = CodeActionPriority.Default; + + return priority; + } + } + + /// + /// Computes the group this code action should be presented in. Legal values + /// this can be must be between and . + /// + /// + /// Values outside of this range will be clamped to be within that range. Requests for may be downgraded to as they + /// poorly behaving high-priority items can cause a negative user experience. + /// + protected virtual CodeActionPriority ComputePriority() + => CodeActionPriority.Default; /// /// Descriptive tags from . @@ -90,7 +130,7 @@ internal virtual ImmutableArray NestedCodeActions /// /// Gets custom tags for the CodeAction. /// - internal ImmutableArray CustomTags { get; private set; } = ImmutableArray.Empty; + internal ImmutableArray CustomTags { get; set; } = ImmutableArray.Empty; /// /// Lazily set provider type that registered this code action. @@ -393,8 +433,14 @@ internal static async Task CleanupDocumentAsync( /// Title of the . /// Function to create the . /// Optional value used to determine the equivalence of the with other s. See . - [SuppressMessage("ApiDesign", "RS0027:Public API with optional parameter(s) should have the most parameters amongst its public overloads.", Justification = "Preserving existing public API")] - public static CodeAction Create(string title, Func> createChangedDocument, string? equivalenceKey = null) + [EditorBrowsable(EditorBrowsableState.Never)] + public static CodeAction Create(string title, Func> createChangedDocument, string? equivalenceKey) + => Create(title, createChangedDocument, equivalenceKey, CodeActionPriority.Default); + + /// + /// Code action priority + [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "This is source compatible")] + public static CodeAction Create(string title, Func> createChangedDocument, string? equivalenceKey = null, CodeActionPriority priority = CodeActionPriority.Default) { if (title == null) { @@ -406,7 +452,7 @@ public static CodeAction Create(string title, Func @@ -416,8 +462,19 @@ public static CodeAction Create(string title, FuncTitle of the . /// Function to create the . /// Optional value used to determine the equivalence of the with other s. See . - [SuppressMessage("ApiDesign", "RS0027:Public API with optional parameter(s) should have the most parameters amongst its public overloads.", Justification = "Preserving existing public API")] - public static CodeAction Create(string title, Func> createChangedSolution, string? equivalenceKey = null) + [EditorBrowsable(EditorBrowsableState.Never)] + public static CodeAction Create(string title, Func> createChangedSolution, string? equivalenceKey) + => Create(title, createChangedSolution, equivalenceKey, CodeActionPriority.Default); + + /// + /// Creates a for a change to more than one within a . + /// Use this factory when the change is expensive to compute and should be deferred until requested. + /// + /// Title of the . + /// Function to create the . + /// Optional value used to determine the equivalence of the with other s. See . + [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "This is source compatible")] + public static CodeAction Create(string title, Func> createChangedSolution, string? equivalenceKey = null, CodeActionPriority priority = CodeActionPriority.Default) { if (title == null) { @@ -429,7 +486,7 @@ public static CodeAction Create(string title, Func @@ -439,10 +496,14 @@ public static CodeAction Create(string title, FuncThe code actions within the group. /// to allow inlining the members of the group into the parent; /// otherwise, to require that this group appear as a group with nested actions. + [EditorBrowsable(EditorBrowsableState.Never)] public static CodeAction Create(string title, ImmutableArray nestedActions, bool isInlinable) => Create(title, nestedActions, isInlinable, priority: CodeActionPriority.Default); - internal static CodeAction Create(string title, ImmutableArray nestedActions, bool isInlinable, CodeActionPriority priority) + /// + /// Priority of the code action + [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "This is source compatible")] + public static CodeAction Create(string title, ImmutableArray nestedActions, bool isInlinable, CodeActionPriority priority = CodeActionPriority.Default) { if (title is null) throw new ArgumentNullException(nameof(title)); @@ -453,48 +514,24 @@ internal static CodeAction Create(string title, ImmutableArray neste return CodeActionWithNestedActions.Create(title, nestedActions, isInlinable, priority); } - internal static CodeAction CreateWithPriority(CodeActionPriority priority, string title, Func> createChangedDocument, string equivalenceKey) - => DocumentChangeAction.Create( - title ?? throw new ArgumentNullException(nameof(title)), - createChangedDocument ?? throw new ArgumentNullException(nameof(createChangedDocument)), - equivalenceKey ?? throw new ArgumentNullException(nameof(equivalenceKey)), - priority); - - internal static CodeAction CreateWithPriority(CodeActionPriority priority, string title, Func> createChangedSolution, string equivalenceKey) - => SolutionChangeAction.Create( - title ?? throw new ArgumentNullException(nameof(title)), - createChangedSolution ?? throw new ArgumentNullException(nameof(createChangedSolution)), - equivalenceKey ?? throw new ArgumentNullException(nameof(equivalenceKey)), - priority); - - internal static CodeAction CreateWithPriority(CodeActionPriority priority, string title, ImmutableArray nestedActions, bool isInlinable) - => CodeActionWithNestedActions.Create( - title ?? throw new ArgumentNullException(nameof(title)), nestedActions, isInlinable, priority); - - internal abstract class SimpleCodeAction : CodeAction - { - protected SimpleCodeAction( + internal abstract class SimpleCodeAction( string title, string? equivalenceKey, CodeActionPriority priority, - bool createdFromFactoryMethod) - { - Title = title; - EquivalenceKey = equivalenceKey; - Priority = priority; - CreatedFromFactoryMethod = createdFromFactoryMethod; - } + bool createdFromFactoryMethod) : CodeAction + { + public sealed override string Title { get; } = title; + public sealed override string? EquivalenceKey { get; } = equivalenceKey; - public sealed override string Title { get; } - public sealed override string? EquivalenceKey { get; } - internal sealed override CodeActionPriority Priority { get; } + protected sealed override CodeActionPriority ComputePriority() + => priority; /// /// Indicates if this CodeAction was created using one of the 'CodeAction.Create' factory methods. /// This is used in to determine the appropriate type /// name to log in the CodeAction telemetry. /// - public bool CreatedFromFactoryMethod { get; } + public bool CreatedFromFactoryMethod { get; } = createdFromFactoryMethod; } internal class CodeActionWithNestedActions : SimpleCodeAction @@ -575,7 +612,7 @@ protected DocumentChangeAction( { } - public static DocumentChangeAction Create( + public static new DocumentChangeAction Create( string title, Func> createChangedDocument, string? equivalenceKey, @@ -610,7 +647,7 @@ protected SolutionChangeAction( { } - public static SolutionChangeAction Create( + public static new SolutionChangeAction Create( string title, Func> createChangedSolution, string? equivalenceKey, diff --git a/src/Workspaces/Core/Portable/CodeActions/CodeActionPriority.cs b/src/Workspaces/Core/Portable/CodeActions/CodeActionPriority.cs index 368a1d91cf504..9167d6f3785c5 100644 --- a/src/Workspaces/Core/Portable/CodeActions/CodeActionPriority.cs +++ b/src/Workspaces/Core/Portable/CodeActions/CodeActionPriority.cs @@ -2,31 +2,47 @@ // 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.CodeFixes; +using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.Diagnostics; +using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.CodeActions +namespace Microsoft.CodeAnalysis.CodeActions; + +/// +/// Priority of a particular code action produced by either a or a . Code actions use priorities to group themselves, with lower priority actions showing +/// up after higher priority ones. Providers should put less relevant code actions into lower priority buckets to +/// have them appear later in the UI, allowing the user to get to important code actions more quickly. +/// +public enum CodeActionPriority { -#pragma warning disable CA1200 // Avoid using cref tags with a prefix /// - /// Internal priority used to bluntly place items in a light bulb in strict orderings. Priorities take - /// the highest precedence when ordering items so that we can ensure very important items get top prominence, - /// and low priority items do not. + /// Lowest priority code actions. Will show up after priority items. /// - /// - /// If is used, the feature that specifies that value should - /// implement and return for , - /// and - /// . This - /// will ensure that the analysis engine runs the providers that will produce those actions first, - /// thus allowing those actions to be computed and displayed prior to running all other providers. - /// - internal enum CodeActionPriority - { - Lowest = 0, - Low = 1, - Medium = 2, - High = 3, + Lowest = 0, - Default = Medium - } + /// + /// Low priority code action. Will show up after priority items. + /// + Low = 1, + + /// + /// Medium priority code action. + /// + Default = 2, + + /// + /// High priority code action. Note: High priority is simply a request on the part of a . + /// The core engine may automatically downgrade these items to priority. + /// + // + // If is used, the analyzer that specifies that value should implement and + // return true for , and and . This will ensure that + // the analysis engine runs the analzyers and providers that will produce those actions first, thus allowing those + // actions to be computed and displayed prior to running all other providers. + // + High = 3, } diff --git a/src/Workspaces/Core/Portable/CodeActions/CodeActionRequestPriority.cs b/src/Workspaces/Core/Portable/CodeActions/CodeActionRequestPriority.cs new file mode 100644 index 0000000000000..dcdffcf23bcdf --- /dev/null +++ b/src/Workspaces/Core/Portable/CodeActions/CodeActionRequestPriority.cs @@ -0,0 +1,84 @@ +// 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.Immutable; +using System.Diagnostics; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CodeActions; + +/// +/// Priority class that a particular or should +/// run at. Providers are run in priority order, allowing the results of higher priority providers to be computed +/// and shown to the user without having to wait on, or share computing resources with, lower priority providers. +/// Providers should choose lower priority classes if they are either: +/// +/// Very slow. Slow providers will impede computing results for other providers in the same priority class. +/// So running in a lower one means that fast providers can still get their results to users quickly. +/// Less relevant. Providers that commonly show available options, but those options are less likely to be +/// taken, should run in lower priority groups. This helps ensure their items are still there when the user wants +/// them, but aren't as prominently shown. +/// +/// +public enum CodeActionRequestPriority +{ + /// + /// Only lowest priority suppression and configuration fix providers should be run. Specifically, providers will be run. NOTE: This priority is reserved for suppression and + /// configuration fix providers and should not be used by regular code fix providers and refactoring providers. + /// + Lowest = 1, + + /// + /// Run the priority below priority. The provider may run slow, or its results may be + /// commonly less relevant for the user. + /// + Low = 2, + + /// + /// Run this provider at default priority. The provider will run in reasonable speeds and provide results that are + /// commonly relevant to the user. + /// + Default = 3, + + /// + /// Run this provider at high priority. Note: High priority is simply a request on the part of a provider. The core + /// engine may automatically downgrade these items to priority. + /// + High = 4, +} + +internal static class CodeActionRequestPriorityExtensions +{ + /// + /// Clamps the value of (which could be any integer) to the legal range of values + /// present in . + /// + public static CodeActionRequestPriority Clamp(this CodeActionRequestPriority priority, ImmutableArray customTags) + { + // Note: we intentionally clamp things lower than 'Low' (including 'Lowest') priorities to 'Low'. The 'Lowest' + // value is only for use by specialized suppression/configuration providers. Any values returned by an actual + // regular provider (either 1st or 3rd party) should still only be between Low and High. + if (priority < CodeActionRequestPriority.Low) + priority = CodeActionRequestPriority.Low; + + if (priority > CodeActionRequestPriority.High) + priority = CodeActionRequestPriority.High; + + if (priority == CodeActionRequestPriority.High && !customTags.Contains(CodeAction.CanBeHighPriorityTag)) + priority = CodeActionRequestPriority.Default; + + return priority; + } + + public static int GetPriorityInt(this CodeActionRequestPriority? priority) + => priority switch + { + null => 0, + { } nonNull => (int)nonNull + }; +} diff --git a/src/Workspaces/Core/Portable/CodeFixes/CodeFixProvider.cs b/src/Workspaces/Core/Portable/CodeFixes/CodeFixProvider.cs index 533ffcb33a4a2..ff5d657a6174b 100644 --- a/src/Workspaces/Core/Portable/CodeFixes/CodeFixProvider.cs +++ b/src/Workspaces/Core/Portable/CodeFixes/CodeFixProvider.cs @@ -2,10 +2,11 @@ // 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.Immutable; +using System.Diagnostics; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CodeFixes { @@ -15,6 +16,8 @@ namespace Microsoft.CodeAnalysis.CodeFixes /// public abstract class CodeFixProvider { + private protected ImmutableArray CustomTags = ImmutableArray.Empty; + /// /// A list of diagnostic IDs that this provider can provide fixes for. /// @@ -38,20 +41,30 @@ public abstract class CodeFixProvider => null; /// - /// What priority this provider should run at. + /// Computes the group this provider should be considered to run at. Legal values + /// this can be must be between and . /// - internal CodeActionRequestPriority RequestPriority + /// + /// Values outside of this range will be clamped to be within that range. Requests for may be downgraded to as they + /// poorly behaving high-priority providers can cause a negative user experience. + /// + protected virtual CodeActionRequestPriority ComputeRequestPriority() + => CodeActionRequestPriority.Default; + + /// + /// Priority class this refactoring provider should run at. Returns if not overridden. Slower, or less relevant, providers should + /// override this and return a lower value to not interfere with computation of normal priority providers. + /// + public CodeActionRequestPriority RequestPriority { get { var priority = ComputeRequestPriority(); - // Note: CodeActionRequestPriority.Lowest is reserved for IConfigurationFixProvider. - Contract.ThrowIfFalse(priority is CodeActionRequestPriority.Low or CodeActionRequestPriority.Normal or CodeActionRequestPriority.High); - return priority; + Debug.Assert(priority is CodeActionRequestPriority.Low or CodeActionRequestPriority.Default or CodeActionRequestPriority.High, "Provider returned invalid priority"); + return priority.Clamp(this.CustomTags); } } - - private protected virtual CodeActionRequestPriority ComputeRequestPriority() - => CodeActionRequestPriority.Normal; } } diff --git a/src/Workspaces/Core/Portable/CodeRefactorings/CodeRefactoringProvider.cs b/src/Workspaces/Core/Portable/CodeRefactorings/CodeRefactoringProvider.cs index 094f9834f3268..66ba98e8d6d5f 100644 --- a/src/Workspaces/Core/Portable/CodeRefactorings/CodeRefactoringProvider.cs +++ b/src/Workspaces/Core/Portable/CodeRefactorings/CodeRefactoringProvider.cs @@ -2,9 +2,10 @@ // 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.Collections.Immutable; +using System.Diagnostics; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CodeRefactorings { @@ -14,6 +15,8 @@ namespace Microsoft.CodeAnalysis.CodeRefactorings /// public abstract class CodeRefactoringProvider { + private protected ImmutableArray CustomTags = ImmutableArray.Empty; + /// /// Computes one or more refactorings for the specified . /// @@ -31,20 +34,30 @@ public abstract class CodeRefactoringProvider => null; /// - /// What priority this provider should run at. + /// Computes the group this provider should be considered to run at. Legal values + /// this can be must be between and . /// - internal CodeActionRequestPriority RequestPriority + /// + /// Values outside of this range will be clamped to be within that range. Requests for may be downgraded to as they + /// poorly behaving high-priority providers can cause a negative user experience. + /// + protected virtual CodeActionRequestPriority ComputeRequestPriority() + => CodeActionRequestPriority.Default; + + /// + /// Priority class this refactoring provider should run at. Returns if not overridden. Slower, or less relevant, providers should + /// override this and return a lower value to not interfere with computation of normal priority providers. + /// + public CodeActionRequestPriority RequestPriority { get { var priority = ComputeRequestPriority(); - // Note: CodeActionRequestPriority.Lowest is reserved for IConfigurationFixProvider. - Contract.ThrowIfFalse(priority is CodeActionRequestPriority.Low or CodeActionRequestPriority.Normal or CodeActionRequestPriority.High); - return priority; + Debug.Assert(priority is CodeActionRequestPriority.Low or CodeActionRequestPriority.Default or CodeActionRequestPriority.High, "Provider returned invalid priority"); + return priority.Clamp(this.CustomTags); } } - - private protected virtual CodeActionRequestPriority ComputeRequestPriority() - => CodeActionRequestPriority.Normal; } } diff --git a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt index 54d2011edcb23..aef4adb547fee 100644 --- a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt @@ -1,7 +1,31 @@ *REMOVED*abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.WithTypeConstraint(Microsoft.CodeAnalysis.SyntaxNode declaration, string typeParameterName, Microsoft.CodeAnalysis.Editing.SpecialTypeConstraintKind kinds, System.Collections.Generic.IEnumerable types = null) -> Microsoft.CodeAnalysis.SyntaxNode *REMOVED*abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.SetAccessorDeclaration(Microsoft.CodeAnalysis.Accessibility accessibility = Microsoft.CodeAnalysis.Accessibility.NotApplicable, System.Collections.Generic.IEnumerable statements = null) -> Microsoft.CodeAnalysis.SyntaxNode +*REMOVED*static Microsoft.CodeAnalysis.CodeActions.CodeAction.Create(string title, System.Func> createChangedDocument, string equivalenceKey = null) -> Microsoft.CodeAnalysis.CodeActions.CodeAction +*REMOVED*static Microsoft.CodeAnalysis.CodeActions.CodeAction.Create(string title, System.Func> createChangedSolution, string equivalenceKey = null) -> Microsoft.CodeAnalysis.CodeActions.CodeAction + +Microsoft.CodeAnalysis.CodeActions.CodeActionRequestPriority.Default = 3 -> Microsoft.CodeAnalysis.CodeActions.CodeActionRequestPriority Microsoft.CodeAnalysis.Editing.SyntaxGenerator.SetAccessorDeclaration(Microsoft.CodeAnalysis.Accessibility accessibility = Microsoft.CodeAnalysis.Accessibility.NotApplicable, System.Collections.Generic.IEnumerable statements = null) -> Microsoft.CodeAnalysis.SyntaxNode Microsoft.CodeAnalysis.Editing.SyntaxGenerator.WithTypeConstraint(Microsoft.CodeAnalysis.SyntaxNode declaration, string typeParameterName, Microsoft.CodeAnalysis.Editing.SpecialTypeConstraintKind kinds, System.Collections.Generic.IEnumerable types = null) -> Microsoft.CodeAnalysis.SyntaxNode +Microsoft.CodeAnalysis.CodeActions.CodeAction.Priority.get -> Microsoft.CodeAnalysis.CodeActions.CodeActionPriority +Microsoft.CodeAnalysis.CodeActions.CodeActionPriority +Microsoft.CodeAnalysis.CodeActions.CodeActionPriority.Default = 2 -> Microsoft.CodeAnalysis.CodeActions.CodeActionPriority +Microsoft.CodeAnalysis.CodeActions.CodeActionPriority.High = 3 -> Microsoft.CodeAnalysis.CodeActions.CodeActionPriority +Microsoft.CodeAnalysis.CodeActions.CodeActionPriority.Low = 1 -> Microsoft.CodeAnalysis.CodeActions.CodeActionPriority +Microsoft.CodeAnalysis.CodeActions.CodeActionPriority.Lowest = 0 -> Microsoft.CodeAnalysis.CodeActions.CodeActionPriority +Microsoft.CodeAnalysis.CodeActions.CodeActionRequestPriority +Microsoft.CodeAnalysis.CodeActions.CodeActionRequestPriority.High = 4 -> Microsoft.CodeAnalysis.CodeActions.CodeActionRequestPriority +Microsoft.CodeAnalysis.CodeActions.CodeActionRequestPriority.Low = 2 -> Microsoft.CodeAnalysis.CodeActions.CodeActionRequestPriority +Microsoft.CodeAnalysis.CodeActions.CodeActionRequestPriority.Lowest = 1 -> Microsoft.CodeAnalysis.CodeActions.CodeActionRequestPriority +Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider.RequestPriority.get -> Microsoft.CodeAnalysis.CodeActions.CodeActionRequestPriority +Microsoft.CodeAnalysis.CodeRefactorings.CodeRefactoringProvider.RequestPriority.get -> Microsoft.CodeAnalysis.CodeActions.CodeActionRequestPriority +static Microsoft.CodeAnalysis.CodeActions.CodeAction.Create(string title, System.Collections.Immutable.ImmutableArray nestedActions, bool isInlinable, Microsoft.CodeAnalysis.CodeActions.CodeActionPriority priority = Microsoft.CodeAnalysis.CodeActions.CodeActionPriority.Default) -> Microsoft.CodeAnalysis.CodeActions.CodeAction +static Microsoft.CodeAnalysis.CodeActions.CodeAction.Create(string title, System.Func> createChangedDocument, string equivalenceKey = null, Microsoft.CodeAnalysis.CodeActions.CodeActionPriority priority = Microsoft.CodeAnalysis.CodeActions.CodeActionPriority.Default) -> Microsoft.CodeAnalysis.CodeActions.CodeAction +static Microsoft.CodeAnalysis.CodeActions.CodeAction.Create(string title, System.Func> createChangedDocument, string equivalenceKey) -> Microsoft.CodeAnalysis.CodeActions.CodeAction +static Microsoft.CodeAnalysis.CodeActions.CodeAction.Create(string title, System.Func> createChangedSolution, string equivalenceKey = null, Microsoft.CodeAnalysis.CodeActions.CodeActionPriority priority = Microsoft.CodeAnalysis.CodeActions.CodeActionPriority.Default) -> Microsoft.CodeAnalysis.CodeActions.CodeAction +static Microsoft.CodeAnalysis.CodeActions.CodeAction.Create(string title, System.Func> createChangedSolution, string equivalenceKey) -> Microsoft.CodeAnalysis.CodeActions.CodeAction +virtual Microsoft.CodeAnalysis.CodeActions.CodeAction.ComputePriority() -> Microsoft.CodeAnalysis.CodeActions.CodeActionPriority +virtual Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider.ComputeRequestPriority() -> Microsoft.CodeAnalysis.CodeActions.CodeActionRequestPriority +virtual Microsoft.CodeAnalysis.CodeRefactorings.CodeRefactoringProvider.ComputeRequestPriority() -> Microsoft.CodeAnalysis.CodeActions.CodeActionRequestPriority Microsoft.CodeAnalysis.Rename.DocumentRenameOptions.Deconstruct(out bool RenameMatchingTypeInStrings, out bool RenameMatchingTypeInComments) -> void Microsoft.CodeAnalysis.Rename.DocumentRenameOptions.Equals(Microsoft.CodeAnalysis.Rename.DocumentRenameOptions other) -> bool Microsoft.CodeAnalysis.Rename.SymbolRenameOptions.Deconstruct(out bool RenameOverloads, out bool RenameInStrings, out bool RenameInComments, out bool RenameFile) -> void diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeActions/CodeActionRequestPriority.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeActions/CodeActionRequestPriority.cs deleted file mode 100644 index fe73a70642643..0000000000000 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeActions/CodeActionRequestPriority.cs +++ /dev/null @@ -1,54 +0,0 @@ -// 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.Diagnostics; - -namespace Microsoft.CodeAnalysis.CodeActions -{ -#pragma warning disable CA1200 // Avoid using cref tags with a prefix - internal enum CodeActionRequestPriority - { - /// - /// No priority specified, all refactoring, code fixes, and analyzers should be run. This is equivalent - /// to , , and combined. - /// - None = 0, - - /// - /// Only lowest priority suppression and configuration fix providers should be run. Specifically, - /// providers will be run. - /// NOTE: This priority is reserved for suppression and configuration fix providers and should not be - /// used by regular code fix providers and refactoring providers. - /// - Lowest = 1, - - /// - /// Only low priority refactoring, code fix providers should be run. Specifically, - /// providers will be run when or - /// is . s - /// which can report at least one fixable diagnostic will be run. - /// - Low = 2, - - /// - /// Only normal priority refactoring, code fix providers should be run. Specifically, - /// providers will be run when or - /// is . s - /// which can report at least one fixable diagnostic will be run. - /// - Normal = 3, - - /// - /// Only high priority refactoring, code fix providers should be run. Specifically, providers will be run when - /// or - /// is . The will be run. - /// - /// Providers that return this should ensure that the appropriate s they return have a of - /// - High = 4, - } -} diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems index b6a831d0cfaa0..1a5bd0607eb49 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems @@ -186,7 +186,6 @@ InternalUtilities\UnicodeCharacterUtilities.cs - diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Diagnostics/IBuiltInAnalyzer.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Diagnostics/IBuiltInAnalyzer.cs index 5631b83b42ffe..6b5b37f508c5c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Diagnostics/IBuiltInAnalyzer.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Diagnostics/IBuiltInAnalyzer.cs @@ -2,7 +2,6 @@ // 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.Simplification; namespace Microsoft.CodeAnalysis.Diagnostics @@ -31,8 +30,8 @@ internal interface IBuiltInAnalyzer bool OpenFileOnly(SimplifierOptions? options); /// - /// What priority this provider should run at. This value is not allowed to be . + /// If this analyzer is privileged and should run with higher priority than other analyzers. /// - CodeActionRequestPriority RequestPriority { get; } + bool IsHighPriority { get; } } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/CustomCodeActions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/CustomCodeActions.cs deleted file mode 100644 index a561ebe63e13b..0000000000000 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/CustomCodeActions.cs +++ /dev/null @@ -1,62 +0,0 @@ -// 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.Threading; -using System.Threading.Tasks; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.CodeActions -{ - // Define dummy priority to avoid ifdefs. - // 'CodeActionPriority' is not a public API, hence not supported in CodeStyle layer. - // https://github.com/dotnet/roslyn/issues/42431 tracks adding a public API. -#if CODE_STYLE - internal enum CodeActionPriority - { - Lowest = 0, - Low = 1, - Medium = 2, - High = 3 - } -#endif - - internal static class CustomCodeActions - { - internal abstract class SimpleCodeAction( - string title, - string? equivalenceKey) : CodeAction - { - public sealed override string Title { get; } = title; - public sealed override string? EquivalenceKey { get; } = equivalenceKey; - } - - internal class DocumentChangeAction( - string title, - Func> createChangedDocument, - string? equivalenceKey, - CodeActionPriority priority) : SimpleCodeAction(title, equivalenceKey) - { - -#if CODE_STYLE - internal CodeActionPriority Priority { get; } = priority; -#else - internal override CodeActionPriority Priority { get; } = priority; - -#endif - - protected sealed override Task GetChangedDocumentAsync(CancellationToken cancellationToken) - => createChangedDocument(cancellationToken); - } - - internal class SolutionChangeAction( - string title, - Func> createChangedSolution, - string? equivalenceKey) : SimpleCodeAction(title, equivalenceKey) - { - protected sealed override Task GetChangedSolutionAsync(CancellationToken cancellationToken) - => createChangedSolution(cancellationToken).AsNullable(); - } - } -} diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/SyntaxEditorBasedCodeFixProvider.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/SyntaxEditorBasedCodeFixProvider.cs index f114fc9e53631..09e2091ab76ad 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/SyntaxEditorBasedCodeFixProvider.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/SyntaxEditorBasedCodeFixProvider.cs @@ -56,7 +56,7 @@ protected void RegisterCodeFix(CodeFixContext context, string title, string equi => context.RegisterCodeFix(CodeAction.Create(title, GetDocumentUpdater(context, diagnostic), equivalenceKey), context.Diagnostics); protected void RegisterCodeFix(CodeFixContext context, string title, string equivalenceKey, CodeActionPriority priority, Diagnostic? diagnostic = null) - => context.RegisterCodeFix(new CustomCodeActions.DocumentChangeAction(title, GetDocumentUpdater(context, diagnostic), equivalenceKey, priority), context.Diagnostics); + => context.RegisterCodeFix(CodeAction.Create(title, GetDocumentUpdater(context, diagnostic), equivalenceKey, priority), context.Diagnostics); protected Func> GetDocumentUpdater(CodeFixContext context, Diagnostic? diagnostic = null) { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceExtensions.projitems b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceExtensions.projitems index 632c78daa9a23..e5c33a810526d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceExtensions.projitems +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceExtensions.projitems @@ -84,7 +84,6 @@ -