未验证 提交 3a3029d8 编写于 作者: C CyrusNajmabadi 提交者: GitHub

Merge pull request #43771 from CyrusNajmabadi/convertTupleToStructOOP

Move convert tuple to struct oop
......@@ -7,10 +7,12 @@
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.ConvertTupleToStruct;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Host.Mef;
namespace Microsoft.CodeAnalysis.CSharp.ConvertTupleToStruct
{
[ExtensionOrder(Before = PredefinedCodeRefactoringProviderNames.IntroduceVariable)]
[ExportLanguageService(typeof(IConvertTupleToStructCodeRefactoringProvider), LanguageNames.CSharp)]
[ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = nameof(PredefinedCodeRefactoringProviderNames.ConvertTupleToStruct)), Shared]
internal class CSharpConvertTupleToStructCodeRefactoringProvider :
AbstractConvertTupleToStructCodeRefactoringProvider<
......@@ -30,12 +32,5 @@ internal class CSharpConvertTupleToStructCodeRefactoringProvider :
public CSharpConvertTupleToStructCodeRefactoringProvider()
{
}
protected override ObjectCreationExpressionSyntax CreateObjectCreationExpression(
NameSyntax nameNode, SyntaxToken openParen, SeparatedSyntaxList<ArgumentSyntax> arguments, SyntaxToken closeParen)
{
return SyntaxFactory.ObjectCreationExpression(
nameNode, SyntaxFactory.ArgumentList(openParen, arguments, closeParen), initializer: null);
}
}
}
......@@ -9,6 +9,7 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.CodeAnalysis.CodeRefactorings;
......@@ -16,8 +17,11 @@
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.GenerateEqualsAndGetHashCodeFromMembers;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Remote;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.CodeAnalysis.Simplification;
......@@ -37,7 +41,7 @@ internal abstract partial class AbstractConvertTupleToStructCodeRefactoringProvi
TTupleTypeSyntax,
TTypeBlockSyntax,
TNamespaceDeclarationSyntax>
: CodeRefactoringProvider
: CodeRefactoringProvider, IConvertTupleToStructCodeRefactoringProvider
where TExpressionSyntax : SyntaxNode
where TNameSyntax : TExpressionSyntax
where TIdentifierNameSyntax : TNameSyntax
......@@ -49,17 +53,6 @@ internal abstract partial class AbstractConvertTupleToStructCodeRefactoringProvi
where TTypeBlockSyntax : SyntaxNode
where TNamespaceDeclarationSyntax : SyntaxNode
{
private enum Scope
{
ContainingMember,
ContainingType,
ContainingProject,
DependentProjects
}
protected abstract TObjectCreationExpressionSyntax CreateObjectCreationExpression(
TNameSyntax nameNode, SyntaxToken openParen, SeparatedSyntaxList<TArgumentSyntax> arguments, SyntaxToken closeParen);
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
{
var (document, textSpan, cancellationToken) = context;
......@@ -75,15 +68,15 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte
// If it does, we can't convert this. There is no way to describe this anonymous type
// in the concrete type we create.
var fields = tupleType.TupleElements;
var containsAnonymousType = fields.Any(p => p.Type.ContainsAnonymousType());
var containsAnonymousType = fields.Any<IFieldSymbol>(p => p.Type.ContainsAnonymousType());
if (containsAnonymousType)
{
return;
}
var capturedTypeParameters =
fields.Select(p => p.Type)
.SelectMany(t => t.GetReferencedTypeParameters())
fields.Select<IFieldSymbol, ITypeSymbol>(p => p.Type)
.SelectMany<ITypeSymbol, ITypeParameterSymbol>(t => t.GetReferencedTypeParameters())
.Distinct()
.ToImmutableArray();
......@@ -112,7 +105,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte
//
// this means we can only find tuples like ```(x: 1, ...)``` but not ```(1, 2)```. The
// latter has members called Item1 and Item2, but those names don't show up in source.
if (fields.All(f => f.CorrespondingTupleField != f))
if (fields.All<IFieldSymbol>(f => f.CorrespondingTupleField != f))
{
scopes.Add(CreateAction(context, Scope.ContainingProject));
scopes.Add(CreateAction(context, Scope.DependentProjects));
......@@ -141,7 +134,7 @@ private static string GetTitle(Scope scope)
_ => throw ExceptionUtilities.UnexpectedValue(scope),
};
private async Task<(SyntaxNode, INamedTypeSymbol)> TryGetTupleInfoAsync(
private static async Task<(SyntaxNode, INamedTypeSymbol)> TryGetTupleInfoAsync(
Document document, TextSpan span, CancellationToken cancellationToken)
{
// Enable refactoring either for TupleExpression or TupleType
......@@ -163,7 +156,59 @@ private static string GetTitle(Scope scope)
return (expressionOrType, tupleType);
}
private async Task<Solution> ConvertToStructAsync(
public async Task<Solution> ConvertToStructAsync(
Document document, TextSpan span, Scope scope, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
using (Logger.LogBlock(FunctionId.AbstractConvertTupleToStructCodeRefactoringProvider_ConvertToStructAsync, cancellationToken))
{
var solution = document.Project.Solution;
var client = await RemoteHostClient.TryGetClientAsync(solution.Workspace, cancellationToken).ConfigureAwait(false);
if (client != null)
{
var resultOpt = await client.TryRunRemoteAsync<SerializableConvertTupleToStructResult>(
WellKnownServiceHubServices.CodeAnalysisService,
nameof(IRemoteConvertTupleToStructCodeRefactoringProvider.ConvertToStructAsync),
solution,
new object[]
{
document.Id,
span,
scope,
},
callbackTarget: null,
cancellationToken).ConfigureAwait(false);
if (resultOpt.HasValue)
{
var result = resultOpt.Value;
var resultSolution = await RemoteUtilities.UpdateSolutionAsync(
solution, result.DocumentTextChanges, cancellationToken).ConfigureAwait(false);
return await AddRenameTokenAsync(
resultSolution, result.RenamedToken, cancellationToken).ConfigureAwait(false);
}
}
}
return await ConvertToStructInCurrentProcessAsync(
document, span, scope, cancellationToken).ConfigureAwait(false);
}
private async Task<Solution> AddRenameTokenAsync(
Solution solution,
(DocumentId documentId, TextSpan span) renamedToken,
CancellationToken cancellationToken)
{
var document = solution.GetDocument(renamedToken.documentId);
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var token = root.FindToken(renamedToken.span.Start);
var newRoot = root.ReplaceToken(token, token.WithAdditionalAnnotations(RenameAnnotation.Create()));
return document.WithSyntaxRoot(newRoot).Project.Solution;
}
private static async Task<Solution> ConvertToStructInCurrentProcessAsync(
Document document, TextSpan span, Scope scope, CancellationToken cancellationToken)
{
var (tupleExprOrTypeNode, tupleType) = await TryGetTupleInfoAsync(
......@@ -187,8 +232,8 @@ private static string GetTitle(Scope scope)
"NewStruct", n => semanticModel.LookupSymbols(position, name: n).IsEmpty);
var capturedTypeParameters =
tupleType.TupleElements.Select(p => p.Type)
.SelectMany(t => t.GetReferencedTypeParameters())
tupleType.TupleElements.Select<IFieldSymbol, ITypeSymbol>(p => p.Type)
.SelectMany<ITypeSymbol, ITypeParameterSymbol>(t => t.GetReferencedTypeParameters())
.Distinct()
.ToImmutableArray();
......@@ -219,7 +264,7 @@ private static string GetTitle(Scope scope)
return updatedSolution;
}
private async Task ReplaceExpressionAndTypesInScopeAsync(
private static async Task ReplaceExpressionAndTypesInScopeAsync(
Dictionary<Document, SyntaxEditor> documentToEditorMap,
ImmutableArray<DocumentToUpdate> documentsToUpdate,
SyntaxNode tupleExprOrTypeNode, INamedTypeSymbol tupleType,
......@@ -355,12 +400,12 @@ private static string GetTitle(Scope scope)
// starting project.
var dependentProjects = graph.GetProjectsThatDirectlyDependOnThisProject(startingProject.Id);
var allProjects = dependentProjects.Select(solution.GetProject)
var allProjects = dependentProjects.Select<ProjectId, Project>(solution.GetProject)
.Where(p => p.SupportsCompilation)
.Concat(startingProject).ToSet();
var result = ArrayBuilder<DocumentToUpdate>.GetInstance();
var tupleFieldNames = tupleType.TupleElements.SelectAsArray(f => f.Name);
var tupleFieldNames = tupleType.TupleElements.SelectAsArray<IFieldSymbol, string>(f => f.Name);
foreach (var project in allProjects)
{
......@@ -375,7 +420,7 @@ private static string GetTitle(Scope scope)
Project project, INamedTypeSymbol tupleType, CancellationToken cancellationToken)
{
var result = ArrayBuilder<DocumentToUpdate>.GetInstance();
var tupleFieldNames = tupleType.TupleElements.SelectAsArray(f => f.Name);
var tupleFieldNames = tupleType.TupleElements.SelectAsArray<IFieldSymbol, string>(f => f.Name);
await AddDocumentsToUpdateForProjectAsync(
project, result, tupleFieldNames, cancellationToken).ConfigureAwait(false);
......@@ -427,7 +472,7 @@ private static bool InfoProbablyContainsTupleFieldNames(SyntaxTreeIndex info, Im
foreach (var group in declarationService.GetDeclarations(typeSymbol).GroupBy(r => r.SyntaxTree))
{
var document = solution.GetDocument(group.Key);
var nodes = group.SelectAsArray(r => r.GetSyntax(cancellationToken));
var nodes = group.SelectAsArray<SyntaxReference, SyntaxNode>(r => r.GetSyntax(cancellationToken));
result.Add(new DocumentToUpdate(document, nodes));
}
......@@ -506,7 +551,7 @@ private static bool InfoProbablyContainsTupleFieldNames(SyntaxTreeIndex info, Im
return currentSolution;
}
private async Task<bool> ReplaceTupleExpressionsAndTypesInDocumentAsync(
private static async Task<bool> ReplaceTupleExpressionsAndTypesInDocumentAsync(
Document document, SyntaxEditor editor, SyntaxNode startingNode,
INamedTypeSymbol tupleType, TNameSyntax fullyQualifiedStructName,
string structName, ImmutableArray<ITypeParameterSymbol> typeParameters,
......@@ -526,7 +571,7 @@ private static bool InfoProbablyContainsTupleFieldNames(SyntaxTreeIndex info, Im
return changed;
}
private async Task<bool> ReplaceMatchingTupleExpressionsAsync(
private static async Task<bool> ReplaceMatchingTupleExpressionsAsync(
Document document, SyntaxEditor editor, SyntaxNode startingNode,
INamedTypeSymbol tupleType, TNameSyntax qualifiedTypeName,
string typeName, ImmutableArray<ITypeParameterSymbol> typeParameters,
......@@ -552,8 +597,7 @@ private static bool InfoProbablyContainsTupleFieldNames(SyntaxTreeIndex info, Im
{
changed = true;
ReplaceWithObjectCreation(
syntaxFacts, editor, typeName, typeParameters,
qualifiedTypeName, startingNode, childCreation);
editor, typeName, typeParameters, qualifiedTypeName, startingNode, childCreation);
}
}
......@@ -583,8 +627,8 @@ private static bool AreEquivalent(StringComparer comparer, INamedTypeSymbol tupl
return true;
}
private void ReplaceWithObjectCreation(
ISyntaxFactsService syntaxFacts, SyntaxEditor editor, string typeName, ImmutableArray<ITypeParameterSymbol> typeParameters,
private static void ReplaceWithObjectCreation(
SyntaxEditor editor, string typeName, ImmutableArray<ITypeParameterSymbol> typeParameters,
TNameSyntax qualifiedTypeName, SyntaxNode startingCreationNode, TTupleExpressionSyntax childCreation)
{
// Use the callback form as tuples types may be nested, and we want to
......@@ -600,33 +644,34 @@ private static bool AreEquivalent(StringComparer comparer, INamedTypeSymbol tupl
? CreateStructNameNode(g, typeName, typeParameters, addRenameAnnotation: true)
: qualifiedTypeName;
var syntaxFacts = g.SyntaxFacts;
syntaxFacts.GetPartsOfTupleExpression<TArgumentSyntax>(
currentTupleExpr, out var openParen, out var arguments, out var closeParen);
arguments = ConvertArguments(syntaxFacts, g, arguments);
arguments = ConvertArguments(g, arguments);
return CreateObjectCreationExpression(typeNameNode, openParen, arguments, closeParen)
return g.ObjectCreationExpression(typeNameNode, openParen, arguments, closeParen)
.WithAdditionalAnnotations(Formatter.Annotation);
});
}
private SeparatedSyntaxList<TArgumentSyntax> ConvertArguments(ISyntaxFactsService syntaxFacts, SyntaxGenerator generator, SeparatedSyntaxList<TArgumentSyntax> arguments)
=> generator.SeparatedList<TArgumentSyntax>(ConvertArguments(syntaxFacts, generator, arguments.GetWithSeparators()));
private static SeparatedSyntaxList<TArgumentSyntax> ConvertArguments(SyntaxGenerator generator, SeparatedSyntaxList<TArgumentSyntax> arguments)
=> generator.SeparatedList<TArgumentSyntax>(ConvertArguments(generator, arguments.GetWithSeparators()));
private SyntaxNodeOrTokenList ConvertArguments(ISyntaxFactsService syntaxFacts, SyntaxGenerator generator, SyntaxNodeOrTokenList list)
=> new SyntaxNodeOrTokenList(list.Select(v => ConvertArgumentOrToken(syntaxFacts, generator, v)));
private static SyntaxNodeOrTokenList ConvertArguments(SyntaxGenerator generator, SyntaxNodeOrTokenList list)
=> new SyntaxNodeOrTokenList(list.Select(v => ConvertArgumentOrToken(generator, v)));
private SyntaxNodeOrToken ConvertArgumentOrToken(ISyntaxFactsService syntaxFacts, SyntaxGenerator generator, SyntaxNodeOrToken arg)
private static SyntaxNodeOrToken ConvertArgumentOrToken(SyntaxGenerator generator, SyntaxNodeOrToken arg)
=> arg.IsToken
? arg
: ConvertArgument(syntaxFacts, generator, (TArgumentSyntax)arg.AsNode());
: ConvertArgument(generator, (TArgumentSyntax)arg.AsNode());
private TArgumentSyntax ConvertArgument(
ISyntaxFactsService syntaxFacts, SyntaxGenerator generator, TArgumentSyntax argument)
private static TArgumentSyntax ConvertArgument(
SyntaxGenerator generator, TArgumentSyntax argument)
{
// Keep named arguments for literal args. It helps keep the code self-documenting.
// Remove for complex args as it's most likely just clutter a person doesn't need
// when instantiating their new type.
var expr = syntaxFacts.GetExpressionOfArgument(argument);
var expr = generator.SyntaxFacts.GetExpressionOfArgument(argument);
if (expr is TLiteralExpressionSyntax)
{
return argument;
......@@ -635,7 +680,7 @@ private SyntaxNodeOrToken ConvertArgumentOrToken(ISyntaxFactsService syntaxFacts
return (TArgumentSyntax)generator.Argument(expr).WithTriviaFrom(argument);
}
private async Task<bool> ReplaceMatchingTupleTypesAsync(
private static async Task<bool> ReplaceMatchingTupleTypesAsync(
Document document, SyntaxEditor editor, SyntaxNode startingNode,
INamedTypeSymbol tupleType, TNameSyntax qualifiedTypeName,
string typeName, ImmutableArray<ITypeParameterSymbol> typeParameters,
......@@ -667,7 +712,7 @@ private SyntaxNodeOrToken ConvertArgumentOrToken(ISyntaxFactsService syntaxFacts
return changed;
}
private void ReplaceWithTypeNode(
private static void ReplaceWithTypeNode(
SyntaxEditor editor, string typeName, ImmutableArray<ITypeParameterSymbol> typeParameters,
TNameSyntax qualifiedTypeName, SyntaxNode startingNode, TTupleTypeSyntax childTupleType)
{
......@@ -751,7 +796,7 @@ private SyntaxNodeOrToken ConvertArgumentOrToken(ISyntaxFactsService syntaxFacts
explicitInterfaceImplementations: default,
WellKnownMemberNames.DeconstructMethodName,
typeParameters: default,
constructor.Parameters.SelectAsArray(p =>
constructor.Parameters.SelectAsArray<IParameterSymbol, IParameterSymbol>(p =>
CodeGenerationSymbolFactory.CreateParameterSymbol(RefKind.Out, p.Type, p.Name)),
assignments);
}
......@@ -763,7 +808,7 @@ private SyntaxNodeOrToken ConvertArgumentOrToken(ISyntaxFactsService syntaxFacts
const string valueName = "value";
var valueNode = generator.IdentifierName(valueName);
var arguments = tupleType.TupleElements.SelectAsArray(
var arguments = tupleType.TupleElements.SelectAsArray<IFieldSymbol, SyntaxNode>(
field => generator.Argument(
generator.MemberAccessExpression(valueNode, field.Name)));
......@@ -810,7 +855,7 @@ private SyntaxNodeOrToken ConvertArgumentOrToken(ISyntaxFactsService syntaxFacts
// For every property, create a corresponding parameter, as well as an assignment
// statement from that parameter to the property.
var parameterToPropMap = new Dictionary<string, ISymbol>();
var parameters = fields.SelectAsArray(field =>
var parameters = fields.SelectAsArray<IFieldSymbol, IParameterSymbol>(field =>
{
var parameter = CodeGenerationSymbolFactory.CreateParameterSymbol(
field.Type, field.Name.ToCamelCase(trimLeadingTypePrefix: false));
......
// 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.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.ConvertTupleToStruct
{
internal interface IConvertTupleToStructCodeRefactoringProvider : ILanguageService
{
Task<Solution> ConvertToStructAsync(
Document document, TextSpan span, Scope scope, CancellationToken cancellationToken);
}
}
// 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.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Remote;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.ConvertTupleToStruct
{
internal interface IRemoteConvertTupleToStructCodeRefactoringProvider
{
Task<SerializableConvertTupleToStructResult> ConvertToStructAsync(
PinnedSolutionInfo solutionInfo,
DocumentId documentId,
TextSpan span,
Scope scope,
CancellationToken cancellationToken);
}
internal class SerializableConvertTupleToStructResult
{
public (DocumentId, TextChange[])[] DocumentTextChanges;
public (DocumentId, TextSpan) RenamedToken;
}
}
// 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.
namespace Microsoft.CodeAnalysis.ConvertTupleToStruct
{
internal enum Scope
{
ContainingMember,
ContainingType,
ContainingProject,
DependentProjects
}
}
......@@ -6,10 +6,12 @@ Imports System.Composition
Imports System.Diagnostics.CodeAnalysis
Imports Microsoft.CodeAnalysis.CodeRefactorings
Imports Microsoft.CodeAnalysis.ConvertTupleToStruct
Imports Microsoft.CodeAnalysis.Host.Mef
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.ConvertTupleToStruct
<ExtensionOrder(Before:=PredefinedCodeRefactoringProviderNames.IntroduceVariable)>
<ExportLanguageService(GetType(IConvertTupleToStructCodeRefactoringProvider), LanguageNames.VisualBasic)>
<ExportCodeRefactoringProvider(LanguageNames.VisualBasic, Name:=PredefinedCodeRefactoringProviderNames.ConvertTupleToStruct), [Shared]>
Friend Class VisualBasicConvertTupleToStructCodeRefactoringProvider
Inherits AbstractConvertTupleToStructCodeRefactoringProvider(Of
......@@ -28,12 +30,5 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ConvertTupleToStruct
<SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification:="Used in test code: https://github.com/dotnet/roslyn/issues/42814")>
Public Sub New()
End Sub
Protected Overrides Function CreateObjectCreationExpression(
nameNode As NameSyntax, openParen As SyntaxToken, arguments As SeparatedSyntaxList(Of ArgumentSyntax), closeParen As SyntaxToken) As ObjectCreationExpressionSyntax
Return SyntaxFactory.ObjectCreationExpression(
attributeLists:=Nothing, nameNode, SyntaxFactory.ArgumentList(openParen, arguments, closeParen), initializer:=Nothing)
End Function
End Class
End Namespace
......@@ -3092,6 +3092,12 @@ public override SyntaxNode ArrayCreationExpression(SyntaxNode elementType, IEnum
public override SyntaxNode ObjectCreationExpression(SyntaxNode type, IEnumerable<SyntaxNode> arguments)
=> SyntaxFactory.ObjectCreationExpression((TypeSyntax)type, CreateArgumentList(arguments), null);
internal override SyntaxNode ObjectCreationExpression(SyntaxNode type, SyntaxToken openParen, SeparatedSyntaxList<SyntaxNode> arguments, SyntaxToken closeParen)
=> SyntaxFactory.ObjectCreationExpression(
(TypeSyntax)type,
SyntaxFactory.ArgumentList(openParen, arguments, closeParen),
initializer: null);
private static ArgumentListSyntax CreateArgumentList(IEnumerable<SyntaxNode> arguments)
=> SyntaxFactory.ArgumentList(CreateArguments(arguments));
......
......@@ -1960,6 +1960,9 @@ public SyntaxNode MemberAccessExpression(SyntaxNode expression, string memberNam
/// </summary>
public abstract SyntaxNode ObjectCreationExpression(SyntaxNode namedType, IEnumerable<SyntaxNode> arguments);
internal abstract SyntaxNode ObjectCreationExpression(
SyntaxNode namedType, SyntaxToken openParen, SeparatedSyntaxList<SyntaxNode> arguments, SyntaxToken closeParen);
/// <summary>
/// Creates an object creation expression.
/// </summary>
......
// 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.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.ConvertTupleToStruct;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Remote
{
// root level service for all Roslyn services
internal partial class CodeAnalysisService : IRemoteConvertTupleToStructCodeRefactoringProvider
{
public Task<SerializableConvertTupleToStructResult> ConvertToStructAsync(
PinnedSolutionInfo solutionInfo,
DocumentId documentId,
TextSpan span,
Scope scope,
CancellationToken cancellationToken)
{
return RunServiceAsync<SerializableConvertTupleToStructResult>(async () =>
{
using (UserOperationBooster.Boost())
{
var solution = await GetSolutionAsync(solutionInfo, cancellationToken).ConfigureAwait(false);
var document = solution.GetDocument(documentId);
var service = document.GetLanguageService<IConvertTupleToStructCodeRefactoringProvider>();
var updatedSolution = await service.ConvertToStructAsync(document, span, scope, cancellationToken).ConfigureAwait(false);
var cleanedSolution = await CleanupAsync(solution, updatedSolution, cancellationToken).ConfigureAwait(false);
var documentTextChanges = await RemoteUtilities.GetDocumentTextChangesAsync(
solution, cleanedSolution, cancellationToken).ConfigureAwait(false);
var renamedToken = await GetRenamedTokenAsync(
solution, cleanedSolution, cancellationToken).ConfigureAwait(false);
return new SerializableConvertTupleToStructResult
{
DocumentTextChanges = documentTextChanges,
RenamedToken = renamedToken,
};
}
}, cancellationToken);
}
private async Task<(DocumentId, TextSpan)> GetRenamedTokenAsync(
Solution oldSolution, Solution newSolution, CancellationToken cancellationToken)
{
var changes = newSolution.GetChangedDocuments(oldSolution);
foreach (var docId in changes)
{
var document = newSolution.GetDocument(docId);
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var renamedToken = root.GetAnnotatedTokens(RenameAnnotation.Kind).FirstOrNull();
if (renamedToken == null)
continue;
return (docId, renamedToken.Value.Span);
}
throw ExceptionUtilities.Unreachable;
}
private async Task<Solution> CleanupAsync(Solution oldSolution, Solution newSolution, CancellationToken cancellationToken)
{
var changes = newSolution.GetChangedDocuments(oldSolution);
var final = newSolution;
foreach (var docId in changes)
{
var cleaned = await CodeAction.CleanupDocumentAsync(
newSolution.GetDocument(docId), cancellationToken).ConfigureAwait(false);
var cleanedRoot = await cleaned.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
final = final.WithDocumentSyntaxRoot(docId, cleanedRoot);
}
return final;
}
}
}
......@@ -487,5 +487,7 @@ internal enum FunctionId
ChangeSignature_Data = 389,
AbstractEncapsulateFieldService_EncapsulateFieldsAsync = 390,
AbstractConvertTupleToStructCodeRefactoringProvider_ConvertToStructAsync = 400,
}
}
......@@ -352,10 +352,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration
Public Overloads Overrides Function ObjectCreationExpression(typeName As SyntaxNode, arguments As IEnumerable(Of SyntaxNode)) As SyntaxNode
Return SyntaxFactory.ObjectCreationExpression(
Nothing,
attributeLists:=Nothing,
DirectCast(typeName, TypeSyntax),
CreateArgumentList(arguments),
Nothing)
initializer:=Nothing)
End Function
Friend Overrides Function ObjectCreationExpression(typeName As SyntaxNode, openParen As SyntaxToken, arguments As SeparatedSyntaxList(Of SyntaxNode), closeParen As SyntaxToken) As SyntaxNode
Return SyntaxFactory.ObjectCreationExpression(
attributeLists:=Nothing,
DirectCast(typeName, TypeSyntax),
SyntaxFactory.ArgumentList(openParen, arguments, closeParen),
initializer:=Nothing)
End Function
Public Overrides Function QualifiedName(left As SyntaxNode, right As SyntaxNode) As SyntaxNode
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册