-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Defer extract method mutation computation till the point the code action is invoked. #70477
Conversation
var codeGenerator = Create(insertionPoint, selectionResult, analyzerResult, options, localFunction); | ||
return codeGenerator.GenerateAsync(cancellationToken); | ||
var codeGenerator = Create(selectionResult, analyzerResult, options, localFunction); | ||
return codeGenerator.GenerateAsync(insertionPoint, cancellationToken); |
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.
insertionpoint is expensive (it forks the document). so it is now passed in as an arg, and only in the "actually create the new method" codepath.
var statements = CreateMethodBody(insertionPointNode, cancellationToken); | ||
var status = CheckActiveStatements(statements); | ||
return status.With(statements.CastArray<SyntaxNode>()); | ||
} |
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.
code refactoring has to still make the new statements, to do some final applicability checks.
we migth be able to optimize this if it's still expensive to do less here in the codepath where we're just querying for applicability.
} | ||
|
||
protected override async Task<SyntaxNode> GenerateBodyForCallSiteContainerAsync( | ||
SyntaxNode context, |
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 the same as SyntaxNode insertionPointNode
. but i acll it context
in some places because that's the original name in the code. i'll fix up to make this all consistent in followup.
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.
renamed in mop up.
|
||
if (statements.Skip(1).Any()) | ||
if (statements.Length != 1) |
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 intentionally not the same as before. the 0 and >=2 cases were handled the same way in the prior code in two separate branches. i merged them into one branch here.
@@ -673,28 +666,26 @@ protected override StatementSyntax CreateAssignmentExpressionStatement(SyntaxTok | |||
.WithUsingKeyword(usingKeyword); | |||
} | |||
|
|||
protected override async Task<GeneratedCode> CreateGeneratedCodeAsync(OperationStatus status, SemanticDocument newDocument, CancellationToken cancellationToken) | |||
protected override async Task<GeneratedCode> CreateGeneratedCodeAsync( |
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.
actual generation of code no long involves 'status' as there's nothing to report once we've decided to generte.
|
||
var actions = await GetCodeActionsAsync(document, textSpan, extractOptions, cleanupOptions, cancellationToken).ConfigureAwait(false); | ||
var actions = await GetCodeActionsAsync(document, textSpan, extractOptions, cancellationToken).ConfigureAwait(false); |
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.
the cleanupoptinos are part of the extractoptions now.
|
||
namespace Microsoft.CodeAnalysis.ExtractMethod | ||
{ | ||
internal abstract class ExtractMethodResult | ||
internal sealed class ExtractMethodResult |
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 subclassing here. just a simple result type.
/// The name token for the invocation node that replaces the extracted code. | ||
/// </summary> | ||
public SyntaxToken? InvocationNameToken { get; } | ||
private readonly AsyncLazy<(Document document, SyntaxToken? invocationNameToken)>? _lazyData; |
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.
computation fo the final doc, and token to rename, is done lazily and cached here.
|
||
var annotation = new SyntaxAnnotation(); | ||
|
||
var root = await DocumentWithoutFinalFormatting.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); |
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.
all this logic moved inside MethodExtractor as the last thing it does in the lazy async callback it passes to the result object.
src/Features/Core/Portable/ExtractMethod/MethodExtractor.CodeGenerator.cs
Show resolved
Hide resolved
src/Features/Core/Portable/ExtractMethod/MethodExtractor.CodeGenerator.cs
Show resolved
Hide resolved
src/Features/Core/Portable/ExtractMethod/MethodExtractor.CodeGenerator.cs
Show resolved
Hide resolved
cancellationToken.ThrowIfCancellationRequested(); | ||
return ExtractMethodResult.Success( | ||
operationStatus, | ||
async cancellationToken => |
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 the crux of hte change. first, we do frontload some statement checks, since tehy are required to know if we're showing the EM option or not. but everything after that moves into this lazy computation in the success object.
GetFormattingRules(documentWithoutFinalFormatting), | ||
generatedCode.MethodNameAnnotation, | ||
generatedCode.MethodDefinitionAnnotation, | ||
cancellationToken).ConfigureAwait(false); |
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.
inlined this method.
Document document, | ||
SyntaxToken? invocationNameToken, | ||
CancellationToken cancellationToken) | ||
{ |
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 was the code that used to be in teh result type.
ExtractOptions = globalOptions.GetExtractMethodOptions(languageServices.Language), | ||
AddImportOptions = globalOptions.GetAddImportPlacementOptions(languageServices), | ||
LineFormattingOptions = globalOptions.GetLineFormattingOptions(languageServices.Language) |
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.
the last two options are redundant. CodeCleanupOptions exposes it.
@ToddGrun this is ready for review. A mop-up PR is coming after this with more code sharing, and fixing of nits. |
var (analyzedDocument, insertionPoint) = await GetAnnotatedDocumentAndInsertionPointAsync( | ||
originalSemanticDocument, analyzeResult, insertionPointNode, cancellationToken).ConfigureAwait(false); | ||
var statements = codeGenerator.GetNewMethodStatements(insertionPointNode, cancellationToken); | ||
if (statements.Status.Failed()) |
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.
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.
they're different. you can be offered one, or the other, or both.
HasRefactoring would still be good though as we could stop once we know about one.
@@ -776,7 +767,7 @@ static bool ReturnOperationBelongsToMethod(SyntaxNode returnOperationSyntax, Syn | |||
|
|||
SemanticDocument CheckReturnOperations( | |||
SyntaxNode node, | |||
OperationStatus<IMethodSymbol> methodSymbolResult, | |||
IMethodSymbol methodSymbol, |
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.
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.
can remove if that's the case :)
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.
intresting. thsi wasn't used. @mavasani a potential bug with the unused parameter analyzer? note: this is in a local function.
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.
Followup to #70466.
Follow-up to #57838. The earlier work deferred only the final formatting and simplification work. This pull request defers additional work on computing the extracted method contents itself.