Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 'priority' concept to our code actions, and code refactoring/fix providers. #68511

Merged
merged 59 commits into from
Jul 17, 2023

Conversation

CyrusNajmabadi
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi commented Jun 9, 2023

Fixes #42431.

This is already a concept we have internally to support prioritizing important work, as well as determining the order to show things in. Not having this has hampered our efforts to move things into the CodeStyle layer (which can only access the public API), as well as allowing analyzer-packs (like roslyn-analyzers) to take advantage of this for their own analyzers.

Note: this PR intentionally does not expose a way for 2nd/3rd parties to provide high priority work. We do not want to do that yet as we are concerned about slow analyzers/providers being added in that then detrimentally impact features like 'add using'. That said, we have designed the code such that a 3rd party could try to go to a higher priority class. Currently, they will be clamped back down to the normal priority class. But if we feel we are ok with allowing this in the future, we can then just change thigns on our end with the 3rd party not having to change their code.

@CyrusNajmabadi CyrusNajmabadi requested a review from mavasani June 9, 2023 18:20
@CyrusNajmabadi CyrusNajmabadi requested review from a team as code owners June 9, 2023 18:20
@dotnet-issue-labeler dotnet-issue-labeler bot added Area-IDE untriaged Issues and PRs which have not yet been triaged by a lead labels Jun 9, 2023
@@ -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;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lowest/Low/Default/High/Highest matches editor naming for this sort of thing (including the lightbulb priority class namign. so i aligned on that for our public API).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i also preferred 'Default' (versus 'Normal' or 'Medium') as it prevented the need to have a duplicate 'Default' member, or to explain what the 'Default' value for these enums was. By just naming it that way, it all simplified (though it does impact the diff here).

private protected override CodeActionRequestPriority ComputeRequestPriority()
=> CodeActionRequestPriority.High;
private protected override CodeActionRequestPriorityInternal ComputeRequestPriorityInternal()
=> CodeActionRequestPriorityInternal.High;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

features that return 'high' use the 'internal' priority system. For now, only we allow a blessed set to be high pri. That set is exactly:

  1. Add using.
  2. Rename
  3. Fix formatting
  4. Fix git merge-conflicts

@@ -40,7 +40,7 @@ protected AbstractBuiltInCodeStyleDiagnosticAnalyzer(ImmutableArray<DiagnosticDe
Debug.Assert(!supportedDiagnostics.Any(descriptor => descriptor.CustomTags.Any(t => t == WellKnownDiagnosticTags.Unnecessary)) || this is AbstractBuiltInUnnecessaryCodeStyleDiagnosticAnalyzer);
}

public virtual CodeActionRequestPriority RequestPriority => CodeActionRequestPriority.Normal;
public virtual CodeActionRequestPriorityInternal RequestPriority => CodeActionRequestPriorityInternal.Normal;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could consider makign the diagnostic-analyzer priority part available as well.

@@ -64,8 +64,6 @@ internal abstract partial class SuggestedAction : ForegroundThreadAffinitizedObj
CodeAction = codeAction;
}

internal virtual CodeActionPriority Priority => CodeAction.Priority;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

never used. removed.

// 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");
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: this is just an assert to catch if we do something wrong ourselves. We dont' want to throw here as our approach is to just clamp. that way someone coudl request a higher pri than currently supported, and we could potentially allow it in the future.

@CyrusNajmabadi CyrusNajmabadi requested a review from sharwell July 14, 2023 18:50
@CyrusNajmabadi
Copy link
Member Author

@mavasani this is ready for another review. Thanks for the suggestion to just have a single public enum for both CodeActionPriority and CodeActionRequestPriority (and to not have any sort of internal matching enum for each). It def cleaned things up.


#if !CODE_STYLE
// Backdoor that allows this provider to use the high-priority bucket.
codeAction.CustomTags = codeAction.CustomTags.Add(CodeAction.CanBeHighPriorityTag);
#endif
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

however, because it is high pri, the feature has to use this backdoor for that to be ok. it will be clamped otherwise.

@CyrusNajmabadi
Copy link
Member Author

@mavasani ptal.

Copy link
Contributor

@mavasani mavasani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, this approach LGTM.

I believe we should allow CodeStyle layer to also specify the high priority custom tag. So basically replace your current code patterns:

#if !CODE_STYLE
            // Backdoor that allows this provider to use the high-priority bucket.
            this.CustomTags = this.CustomTags.Add(CodeAction.CanBeHighPriorityTag);
#endif

with

            // Backdoor that allows this provider to use the high-priority bucket.
#if !CODE_STYLE
            this.CustomTags = this.CustomTags.Add(CodeAction.CanBeHighPriorityTag);
#else
            // Reflection based logic to add `CanBeHighPriorityTag` to `CustomTags` property.
#endif

and at this point, we probably don't need to do this via CustomTags property, but can have a new internal bool property to indicate "please don't clamp me". It's your call on choosing the most preferred implementation though.

@CyrusNajmabadi
Copy link
Member Author

It's your call on choosing the most preferred implementation though.

I'll try this today.

Copy link
Contributor

@mavasani mavasani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We discussed this offline. This PR is not regressing any existing functionality, as code action priority has always been an internal concept which was never available in the CodeStyle layer. We would eventually like CodeStyle layer to also be able to supply these priorities, but that would be a new feature request. Given that default VS customers who do not explicitly reference MS.CA.CSharp.CodeStyle NuGet package are not affected here, adding this feature would be a slightly lower priority. If we do decide to take it up, reflection would be a possible approach.

@CyrusNajmabadi CyrusNajmabadi enabled auto-merge July 17, 2023 16:18
@CyrusNajmabadi CyrusNajmabadi merged commit 04131b9 into dotnet:main Jul 17, 2023
@ghost ghost added this to the Next milestone Jul 17, 2023
@CyrusNajmabadi CyrusNajmabadi deleted the codeActionPri branch July 18, 2023 01:24
@allisonchou allisonchou modified the milestones: Next, 17.8 P1 Jul 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-IDE untriaged Issues and PRs which have not yet been triaged by a lead
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Expose internal API CodeAction.CodeActionPriority
4 participants