-
Notifications
You must be signed in to change notification settings - Fork 4k
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 FixAll support for code refactorings (approach #2) #60906
Conversation
…to add FixAll support
…ks making them public in future. Also move some helper types from shared layer to workspaces layer as a result of internalizing these APIs
… up the added fix all integration tests
Closes dotnet#32461 No public APIs are being added in this PR. dotnet#60703 tracks designing and exposing public APIs for this feature.
…/roslyn into FixAllCodeRefactoring_2
At this point i'm overall very happy with the state of things. The last set of comments is mostly cleanup/docs (Though there are at least a couple of important things i think should be looked at closely). Once that's addressed, i think we're good to go in :) |
Yes, that is correct. But we do the computation now at the point where we are just about to invoke the refactoring provider to fix all instances in the document, and the provider is going to request for syntax nodes to compute the fixes, so we are not really saving anything. |
This is not actually true. Consider if we needed to do fix-all on 100s to thousands of files. This will go through and parse them all (just to find the length). However, these trees are not held onto by anything. Internally, we just have a weak-reference (so that if one is alive, and another asks for it, they'll get the same tree). So if a single GC happens (which is virtually guaranteed in a fix-all scenario), then all those trees will be garbage collected and we'll have to parse them all over again as we actually fix each file. You're only parsing/loading-text just to say "i want to do the whole file". there's got to be a cleaner way of representing that doesn't involve pre-parsing and potentially redoing all that work later :) |
The only other alternative is to pass in an empty array of spans to the refactoring provider, and it needs to check for empty array and deduce that it needs to fix the entire document. If that is fine to keep as a contract, we can skip this part in the engine. |
Right. FOr each document it could have a Nullable span, where null means: the entire doc. |
The API actually takes an immutable array of spans, as we have to account for partial definitions for ContainingType fix all scope. Probably we can pass in an Optional immutable array. I’ll go ahead with that. |
@CyrusNajmabadi I was able to avoid explicitly parsing/loading the document text for this scenario with 265022a. The final call made to each refactoring provider has a SyntaxEditor, which already has the root, so we perform the empty spans check right before that call and insert the full span. |
/// </summary> | ||
internal abstract class AbstractFixAllCodeAction : CodeAction | ||
{ | ||
private bool _showPreviewChangesDialog; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mutable field is very strange. but if this is how it already was, then that's ok.
@@ -427,8 +428,14 @@ private static bool IsBulkConfigurationAction(CodeAction action) | |||
|
|||
var filteredRefactorings = FilterOnAnyThread(refactorings, selection, filterOutsideSelection); | |||
|
|||
return filteredRefactorings.SelectAsArray( | |||
r => OrganizeRefactorings(workspace, r)); | |||
using var _ = ArrayBuilder<UnifiedSuggestedActionSet>.GetInstance(filteredRefactorings.Length, out var orderedRefactorings); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no biggy, but there's also SelectAsArrayAsync
break; | ||
|
||
case FixAllKind.Refactoring: | ||
functionId = FunctionId.Refactoring_FixAllOccurrencesContext; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tbh, i'm not sure if we care that much, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we're already going to say which provider we have... so we already distinguish on what matters. having a distinct id to bucket fix all for fixes vs refactorings doesn't seem relevant (afaict).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me sync up with @mikadumont if we care about differentiating between a FixAll for code fixes vs FixAll for refactorings with respect to telemetry. For example, do we want to find out how many FixAll for refactorings were invoked by users over a time frame?
} | ||
|
||
public override bool CanBeFixed(Diagnostic diagnostic) | ||
=> throw ExceptionUtilities.Unreachable; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should be doc'ed. that's basically my preference for anything with inheritance where we say something is unreachable. it's not always evident, so the docs should help the next person understand why we think this.
IFixAllState State { get; } | ||
IFixAllProvider FixAllProvider { get; } | ||
Solution Solution { get; } | ||
Project Project { get; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit, SOlution seems redundant (can be =>
property with => Project.Solution
;.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh, we don't have DIM. ignore this. it could technically be an extension method. but that's also not ideal. oh well.
FixAllKind FixAllKind { get; } | ||
Document? Document { get; } | ||
Project Project { get; } | ||
Solution Solution { get; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same comment here.
/// <summary> | ||
/// Underlying code fix provider or code refactoring provider for the fix all occurrences fix. | ||
/// </summary> | ||
object Provider { get; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit, woudl be nice to have common internal base interface (Even if empty) to make sure we're passing in the right type of thing, and not some random value.
/// </remarks> | ||
internal abstract class FixAllProvider : IFixAllProvider | ||
{ | ||
private protected static ImmutableArray<FixAllScope> DefaultSupportedFixAllScopes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private protected static ImmutableArray<FixAllScope> DefaultSupportedFixAllScopes | |
private protected static readonly ImmutableArray<FixAllScope> DefaultSupportedFixAllScopes |
<None Include="$(MSBuildThisFileDirectory)WorkspaceExtensionsResources.resx" /> | ||
<None Include="$(MSBuildThisFileDirectory)WorkspaceExtensionsResources.resx"> | ||
<SubType>Designer</SubType> | ||
</None> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
odd change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will revert, this keeps coming back.
@@ -43,7 +43,6 @@ | |||
<Compile Include="$(MSBuildThisFileDirectory)Indentation\VisualBasicIndentationService.vb" /> | |||
<Compile Include="$(MSBuildThisFileDirectory)LanguageServices\VisualBasicBlockFactsService.vb" /> | |||
<Compile Include="$(MSBuildThisFileDirectory)LanguageServices\VisualBasicFileBannerFactsService.vb" /> | |||
<Compile Include="$(MSBuildThisFileDirectory)LanguageServices\VisualBasicFixAllSpanMappingService.vb" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
where did this go?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not required in the CodeStyle layer, so moved to the Workspaces layer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
awesome! if you think any of this needs cleanup, feel free to do in a future PR.
Thanks, I have been struggling to get the tests passing on the PR, so would like to merge this. I plan to immediately do a follow-up PR for #60956. I'll address the latest round of feedback as part of it. |
Closes #32461
No public APIs are being added in this PR. #60703 tracks designing and exposing public APIs for this feature.