提交 9a777fff 编写于 作者: C CyrusNajmabadi

Add parameter should respect multi-line paraemters.

上级 4902a12a
......@@ -230,5 +230,116 @@ void M()
}
}");
}
[WorkItem(20708, "https://github.com/dotnet/roslyn/issues/20708")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddParameter)]
public async Task TestMultiLineParameters1()
{
await TestInRegularAndScriptAsync(
@"
class C
{
public C(int i,
/* foo */ int j)
{
}
private void Foo()
{
new [|C|](true, 0, 0);
}
}",
@"
class C
{
public C(bool v,
int i,
/* foo */ int j)
{
}
private void Foo()
{
new C(true, 0, 0);
}
}",
ignoreTrivia: false);
}
[WorkItem(20708, "https://github.com/dotnet/roslyn/issues/20708")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddParameter)]
public async Task TestMultiLineParameters2()
{
await TestInRegularAndScriptAsync(
@"
class C
{
public C(int i,
/* foo */ int j)
{
}
private void Foo()
{
new [|C|](0, true, 0);
}
}",
@"
class C
{
public C(int i,
bool v,
/* foo */ int j)
{
}
private void Foo()
{
new C(0, true, 0);
}
}",
ignoreTrivia: false);
}
[WorkItem(20708, "https://github.com/dotnet/roslyn/issues/20708")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddParameter)]
public async Task TestMultiLineParameters3()
{
await TestInRegularAndScriptAsync(
@"
class C
{
public C(int i,
/* foo */ int j)
{
}
private void Foo()
{
new [|C|](0, 0, true);
}
}",
@"
class C
{
public C(int i,
/* foo */ int j,
bool v)
{
}
private void Foo()
{
new C(0, 0, true);
}
}",
ignoreTrivia: false);
}
}
}
......@@ -200,5 +200,89 @@ class D
end class
")
End Function
<WorkItem(20708, "https://github.com/dotnet/roslyn/issues/20708")>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddParameter)>
Public Async Function TestMultiLineParameters1() As Task
Await TestInRegularAndScriptAsync(
"
class C
public sub new(i as integer,
j as integer)
end sub
private sub Foo()
dim x = new C(true, 0, [|0|])
end sub
end class",
"
class C
public sub new(v As Boolean,
i as integer,
j as integer)
end sub
private sub Foo()
dim x = new C(true, 0, 0)
end sub
end class",
ignoreTrivia:=False)
End Function
<WorkItem(20708, "https://github.com/dotnet/roslyn/issues/20708")>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddParameter)>
Public Async Function TestMultiLineParameters2() As Task
Await TestInRegularAndScriptAsync(
"
class C
public sub new(i as integer,
j as integer)
end sub
private sub Foo()
dim x = new C(0, true, [|0|])
end sub
end class",
"
class C
public sub new(i as integer,
v As Boolean,
j as integer)
end sub
private sub Foo()
dim x = new C(0, true, 0)
end sub
end class",
ignoreTrivia:=False)
End Function
<WorkItem(20708, "https://github.com/dotnet/roslyn/issues/20708")>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddParameter)>
Public Async Function TestMultiLineParameters3() As Task
Await TestInRegularAndScriptAsync(
"
class C
public sub new(i as integer,
j as integer)
end sub
private sub Foo()
dim x = new C(0, 0, [|true|])
end sub
end class",
"
class C
public sub new(i as integer,
j as integer,
v As Boolean)
end sub
private sub Foo()
dim x = new C(0, 0, true)
end sub
end class",
ignoreTrivia:=False)
End Function
End Class
End Namespace
......@@ -10,6 +10,7 @@
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
......@@ -169,56 +170,49 @@ private int NonParamsParameterCount(IMethodSymbol method)
SeparatedSyntaxList<TArgumentSyntax> argumentList,
CancellationToken cancellationToken)
{
var generator = SyntaxGenerator.GetGenerator(invocationDocument.Project.Solution.Workspace, method.Language);
var methodDeclaration = await method.DeclaringSyntaxReferences[0].GetSyntaxAsync(cancellationToken).ConfigureAwait(false);
var syntaxFacts = invocationDocument.GetLanguageService<ISyntaxFactsService>();
var semanticFacts = invocationDocument.GetLanguageService<ISemanticFactsService>();
var argumentName = syntaxFacts.GetNameForArgument(argument);
var expression = syntaxFacts.GetExpressionOfArgument(argument);
var semanticModel = await invocationDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var parameterType = semanticModel.GetTypeInfo(expression).Type ?? semanticModel.Compilation.ObjectType;
var newMethodDeclaration = GetNewMethodDeclaration(
method, argument, argumentList, generator, methodDeclaration,
semanticFacts, argumentName, expression, semanticModel,
parameterType, cancellationToken);
var root = methodDeclaration.SyntaxTree.GetRoot(cancellationToken);
var newRoot = root.ReplaceNode(methodDeclaration, newMethodDeclaration);
var (parameterSymbol, isNamedArgument) = await CreateParameterSymbolAsync(
invocationDocument, method, argument, cancellationToken).ConfigureAwait(false);
var methodDocument = invocationDocument.Project.Solution.GetDocument(methodDeclaration.SyntaxTree);
var syntaxFacts = methodDocument.GetLanguageService<ISyntaxFactsService>();
var methodDeclarationRoot = methodDeclaration.SyntaxTree.GetRoot(cancellationToken);
var editor = new SyntaxEditor(methodDeclarationRoot, methodDocument.Project.Solution.Workspace);
var parameterDeclaration = editor.Generator.ParameterDeclaration(parameterSymbol)
.WithAdditionalAnnotations(Formatter.Annotation);
AddParameter(
syntaxFacts, editor, methodDeclaration, isNamedArgument, argument,
argumentList, parameterDeclaration, cancellationToken);
var newRoot = editor.GetChangedRoot();
var newDocument = methodDocument.WithSyntaxRoot(newRoot);
return newDocument;
}
private static SyntaxNode GetNewMethodDeclaration(
private async Task<(IParameterSymbol, bool isNamedArgument)> CreateParameterSymbolAsync(
Document invocationDocument,
IMethodSymbol method,
TArgumentSyntax argument,
SeparatedSyntaxList<TArgumentSyntax> argumentList,
SyntaxGenerator generator,
SyntaxNode declaration,
ISemanticFactsService semanticFacts,
string argumentName,
SyntaxNode expression,
SemanticModel semanticModel,
ITypeSymbol parameterType,
CancellationToken cancellationToken)
{
var syntaxFacts = invocationDocument.GetLanguageService<ISyntaxFactsService>();
var semanticFacts = invocationDocument.GetLanguageService<ISemanticFactsService>();
var argumentName = syntaxFacts.GetNameForArgument(argument);
var expression = syntaxFacts.GetExpressionOfArgument(argument);
var semanticModel = await invocationDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var parameterType = semanticModel.GetTypeInfo(expression).Type ?? semanticModel.Compilation.ObjectType;
if (!string.IsNullOrWhiteSpace(argumentName))
{
var newParameterSymbol = CodeGenerationSymbolFactory.CreateParameterSymbol(
attributes: default(ImmutableArray<AttributeData>),
refKind: RefKind.None,
isParams: false,
type: parameterType,
name: argumentName);
var newParameterDeclaration = generator.ParameterDeclaration(newParameterSymbol);
return generator.AddParameters(declaration, new[] { newParameterDeclaration });
attributes: default, refKind: RefKind.None, isParams: false, type: parameterType, name: argumentName);
return (newParameterSymbol, isNamedArgument: true);
}
else
{
......@@ -227,19 +221,133 @@ private int NonParamsParameterCount(IMethodSymbol method)
var uniqueName = NameGenerator.EnsureUniqueness(name, method.Parameters.Select(p => p.Name));
var newParameterSymbol = CodeGenerationSymbolFactory.CreateParameterSymbol(
attributes: default(ImmutableArray<AttributeData>),
refKind: RefKind.None,
isParams: false,
type: parameterType,
name: uniqueName);
var argumentIndex = argumentList.IndexOf(argument);
var newParameterDeclaration = generator.ParameterDeclaration(newParameterSymbol);
return generator.InsertParameters(
declaration, argumentIndex, new[] { newParameterDeclaration });
attributes: default, refKind: RefKind.None, isParams: false, type: parameterType, name: uniqueName);
return (newParameterSymbol, isNamedArgument: false);
}
}
private static void AddParameter(
ISyntaxFactsService syntaxFacts,
SyntaxEditor editor,
SyntaxNode declaration,
bool isNamedArgument,
TArgumentSyntax argument,
SeparatedSyntaxList<TArgumentSyntax> argumentList,
SyntaxNode parameterDeclaration,
CancellationToken cancellationToken)
{
var generator = editor.Generator;
var existingParameters = generator.GetParameters(declaration);
var placeOnNewLine = ShouldPlaceParametersOnNewLine(existingParameters, cancellationToken);
var argumentIndex = argumentList.IndexOf(argument);
if (isNamedArgument || argumentIndex == existingParameters.Count)
{
if (placeOnNewLine)
{
var leadingIndentation = GetDesiredLeadingIndentation(
generator, syntaxFacts, existingParameters.Last(), includeLeadingNewLine: true);
parameterDeclaration = parameterDeclaration.WithPrependedLeadingTrivia(leadingIndentation)
.WithAdditionalAnnotations(Formatter.Annotation);
}
editor.AddParameter(declaration, parameterDeclaration);
}
else
{
if (placeOnNewLine)
{
if (argumentIndex == 0)
{
// Have to move the next parameter to the next line.
editor.InsertParameter(declaration, argumentIndex, parameterDeclaration);
var nextParameter = existingParameters[argumentIndex];
var leadingIndentation = GetDesiredLeadingIndentation(
generator, syntaxFacts, existingParameters[argumentIndex + 1], includeLeadingNewLine: true);
editor.ReplaceNode(
nextParameter,
nextParameter.WithPrependedLeadingTrivia(leadingIndentation)
.WithAdditionalAnnotations(Formatter.Annotation));
}
else
{
var nextParameter = existingParameters[argumentIndex];
var leadingIndentation = GetDesiredLeadingIndentation(
generator, syntaxFacts, existingParameters[argumentIndex], includeLeadingNewLine: false);
parameterDeclaration = parameterDeclaration.WithPrependedLeadingTrivia(leadingIndentation);
editor.InsertParameter(declaration, argumentIndex, parameterDeclaration);
editor.ReplaceNode(
nextParameter,
nextParameter.WithPrependedLeadingTrivia(generator.ElasticCarriageReturnLineFeed)
.WithAdditionalAnnotations(Formatter.Annotation));
}
}
else
{
editor.InsertParameter(declaration, argumentIndex, parameterDeclaration);
}
}
}
private static List<SyntaxTrivia> GetDesiredLeadingIndentation(
SyntaxGenerator generator, ISyntaxFactsService syntaxFacts,
SyntaxNode node, bool includeLeadingNewLine)
{
var triviaList = new List<SyntaxTrivia>();
if (includeLeadingNewLine)
{
triviaList.Add(generator.ElasticCarriageReturnLineFeed);
}
var lastWhitespace = default(SyntaxTrivia);
foreach(var trivia in node.GetLeadingTrivia().Reverse())
{
if (syntaxFacts.IsWhitespaceTrivia(trivia))
{
lastWhitespace = trivia;
}
else if (syntaxFacts.IsEndOfLineTrivia(trivia))
{
break;
}
}
if (lastWhitespace.RawKind != 0)
{
triviaList.Add(lastWhitespace);
}
return triviaList;
}
private static bool ShouldPlaceParametersOnNewLine(
IReadOnlyList<SyntaxNode> parameters, CancellationToken cancellationToken)
{
if (parameters.Count <= 1)
{
return false;
}
var text = parameters[0].SyntaxTree.GetText(cancellationToken);
for (int i = 1, n = parameters.Count; i < n; i++)
{
var lastParameter = parameters[i - 1];
var thisParameter = parameters[i];
if (text.AreOnSameLine(lastParameter.GetLastToken(), thisParameter.GetFirstToken()))
{
return false;
}
}
// All parameters are on different lines. Place the new parameter on a new line as well.
return true;
}
private static readonly SymbolDisplayFormat SimpleFormat =
new SymbolDisplayFormat(
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameOnly,
......
......@@ -19,6 +19,7 @@ namespace Microsoft.CodeAnalysis.CSharp.CodeGeneration
[ExportLanguageService(typeof(SyntaxGenerator), LanguageNames.CSharp), Shared]
internal class CSharpSyntaxGenerator : SyntaxGenerator
{
internal override SyntaxTrivia ElasticCarriageReturnLineFeed => SyntaxFactory.ElasticCarriageReturnLineFeed;
internal override SyntaxTrivia CarriageReturnLineFeed => SyntaxFactory.CarriageReturnLineFeed;
internal override bool RequiresExplicitImplementationForInterfaceMembers => false;
......
......@@ -64,6 +64,11 @@ public static void AddParameter(this SyntaxEditor editor, SyntaxNode declaration
editor.ReplaceNode(declaration, (d, g) => g.AddParameters(d, new[] { parameter }));
}
public static void InsertParameter(this SyntaxEditor editor, SyntaxNode declaration, int index, SyntaxNode parameter)
{
editor.ReplaceNode(declaration, (d, g) => g.InsertParameters(d, index, new[] { parameter }));
}
public static void AddAttribute(this SyntaxEditor editor, SyntaxNode declaration, SyntaxNode attribute)
{
editor.ReplaceNode(declaration, (d, g) => g.AddAttributes(d, new[] { attribute }));
......
......@@ -31,6 +31,7 @@ public abstract class SyntaxGenerator : ILanguageService
public static SyntaxRemoveOptions DefaultRemoveOptions = SyntaxRemoveOptions.KeepUnbalancedDirectives | SyntaxRemoveOptions.AddElasticMarker;
internal abstract SyntaxTrivia CarriageReturnLineFeed { get; }
internal abstract SyntaxTrivia ElasticCarriageReturnLineFeed { get; }
internal abstract bool RequiresExplicitImplementationForInterfaceMembers { get; }
internal abstract SyntaxTrivia EndOfLine(string text);
......
......@@ -16,6 +16,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration
Public Shared ReadOnly Instance As SyntaxGenerator = New VisualBasicSyntaxGenerator()
Friend Overrides ReadOnly Property ElasticCarriageReturnLineFeed As SyntaxTrivia = SyntaxFactory.ElasticCarriageReturnLineFeed
Friend Overrides ReadOnly Property CarriageReturnLineFeed As SyntaxTrivia = SyntaxFactory.CarriageReturnLineFeed
Friend Overrides ReadOnly Property RequiresExplicitImplementationForInterfaceMembers As Boolean = True
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册