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 FixAll support for code refactorings (approach #2) #60906

Merged
merged 38 commits into from
Apr 29, 2022

Conversation

mavasani
Copy link
Contributor

@mavasani mavasani commented Apr 22, 2022

Closes #32461

No public APIs are being added in this PR. #60703 tracks designing and exposing public APIs for this feature.

FixAllForRefactorings

…ks making them public in future.

Also move some helper types from shared layer to workspaces layer as a result of internalizing these APIs
Closes dotnet#32461

No public APIs are being added in this PR. dotnet#60703 tracks designing and exposing public APIs for this feature.
@CyrusNajmabadi
Copy link
Member

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 :)

@mavasani
Copy link
Contributor Author

note: you can still skip getting the root. you can figure the length out from teh text of the document.

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.

@CyrusNajmabadi
Copy link
Member

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 :)

@mavasani
Copy link
Contributor Author

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.

@CyrusNajmabadi
Copy link
Member

The only other alternative is to pass in an empty array of spans to the refactoring provider

Right. FOr each document it could have a Nullable span, where null means: the entire doc.

@mavasani
Copy link
Contributor Author

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.

@mavasani
Copy link
Contributor Author

@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;
Copy link
Member

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

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;
Copy link
Member

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?

Copy link
Member

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).

Copy link
Contributor Author

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;
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi Apr 29, 2022

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; }
Copy link
Member

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;.

Copy link
Member

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; }
Copy link
Member

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; }
Copy link
Member

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
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
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>
Copy link
Member

Choose a reason for hiding this comment

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

odd change.

Copy link
Contributor Author

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

Choose a reason for hiding this comment

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

where did this go?

Copy link
Contributor Author

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.

Copy link
Member

@CyrusNajmabadi CyrusNajmabadi left a 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.

@mavasani
Copy link
Contributor Author

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.

@mavasani mavasani merged commit 18305a1 into dotnet:main Apr 29, 2022
@ghost ghost added this to the Next milestone Apr 29, 2022
@mavasani mavasani deleted the FixAllCodeRefactoring_2 branch April 29, 2022 16:21
@Cosifne Cosifne modified the milestones: Next, 17.3 P2 May 31, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

provide fix all ability to CodeRefactoringProvider
3 participants