diff --git a/src/EditorFeatures/CSharpTest/AddParameter/AddParameterTests.cs b/src/EditorFeatures/CSharpTest/AddParameter/AddParameterTests.cs index 344d0e0e6a3d6c03650bb03c04c85ad8ec73477f..67a162a9895e8d05760bf254c707958a1cc6a58b 100644 --- a/src/EditorFeatures/CSharpTest/AddParameter/AddParameterTests.cs +++ b/src/EditorFeatures/CSharpTest/AddParameter/AddParameterTests.cs @@ -339,6 +339,123 @@ private void Foo() new C(0, 0, true); } }", +ignoreTrivia: false); + } + + [WorkItem(20708, "https://github.com/dotnet/roslyn/issues/20708")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddParameter)] + public async Task TestMultiLineParameters4() + { + 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 TestMultiLineParameters5() + { + 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 TestMultiLineParameters6() + { + 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); } } diff --git a/src/Features/Core/Portable/AddParameter/AbstractAddParameterCodeFixProvider.cs b/src/Features/Core/Portable/AddParameter/AbstractAddParameterCodeFixProvider.cs index 7b4e06f88a97ef611b3191f0a1427502318e6411..44832e06f8028d5611c6ce4ec421bacee516fcde 100644 --- a/src/Features/Core/Portable/AddParameter/AbstractAddParameterCodeFixProvider.cs +++ b/src/Features/Core/Portable/AddParameter/AbstractAddParameterCodeFixProvider.cs @@ -183,9 +183,14 @@ private int NonParamsParameterCount(IMethodSymbol method) var parameterDeclaration = editor.Generator.ParameterDeclaration(parameterSymbol) .WithAdditionalAnnotations(Formatter.Annotation); + var existingParameters = editor.Generator.GetParameters(methodDeclaration); + var insertionIndex = isNamedArgument + ? existingParameters.Count + : argumentList.IndexOf(argument); + AddParameter( - syntaxFacts, editor, methodDeclaration, isNamedArgument, argument, - argumentList, parameterDeclaration, cancellationToken); + syntaxFacts, editor, methodDeclaration, argument, + insertionIndex, parameterDeclaration, cancellationToken); var newRoot = editor.GetChangedRoot(); var newDocument = methodDocument.WithSyntaxRoot(newRoot); @@ -231,19 +236,18 @@ private int NonParamsParameterCount(IMethodSymbol method) ISyntaxFactsService syntaxFacts, SyntaxEditor editor, SyntaxNode declaration, - bool isNamedArgument, TArgumentSyntax argument, - SeparatedSyntaxList argumentList, + int insertionIndex, SyntaxNode parameterDeclaration, CancellationToken cancellationToken) { + var sourceText = declaration.SyntaxTree.GetText(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 (insertionIndex == existingParameters.Count) { if (placeOnNewLine) { @@ -262,21 +266,52 @@ private int NonParamsParameterCount(IMethodSymbol method) // Inserting the parameter somewhere other than the end. if (placeOnNewLine) { - if (argumentIndex == 0) + if (insertionIndex == 0) { - // We want to insert the parameter at the front of the exsiting parameter - // list. That means we need to move the current first parameter to a new - // line. Give the current first parameter the indentation of the second - // parameter in the list. - editor.InsertParameter(declaration, argumentIndex, parameterDeclaration); - var nextParameter = existingParameters[argumentIndex]; + var firstParameter = existingParameters[0]; + var previousToken = firstParameter.GetFirstToken().GetPreviousToken(); - var leadingIndentation = GetDesiredLeadingIndentation( - generator, syntaxFacts, existingParameters[argumentIndex + 1], includeLeadingNewLine: true); - editor.ReplaceNode( - nextParameter, - nextParameter.WithPrependedLeadingTrivia(leadingIndentation) - .WithAdditionalAnnotations(Formatter.Annotation)); + if (sourceText.AreOnSameLine(previousToken, firstParameter.GetFirstToken())) + { + // First parameter is on hte same line as the method. + + // We want to insert the parameter at the front of the exsiting parameter + // list. That means we need to move the current first parameter to a new + // line. Give the current first parameter the indentation of the second + // parameter in the list. + editor.InsertParameter(declaration, insertionIndex, parameterDeclaration); + var nextParameter = existingParameters[insertionIndex]; + + var nextLeadingIndentation = GetDesiredLeadingIndentation( + generator, syntaxFacts, existingParameters[insertionIndex + 1], includeLeadingNewLine: true); + editor.ReplaceNode( + nextParameter, + nextParameter.WithPrependedLeadingTrivia(nextLeadingIndentation) + .WithAdditionalAnnotations(Formatter.Annotation)); + } + else + { + // First parameter is on its own line. No need to adjust its indentation. + // Just copy its indentation over to the parameter we're inserting, and + // make sure the current first parameter gets a newline so it stays on + // its own line. + + // We want to insert the parameter at the front of the exsiting parameter + // list. That means we need to move the current first parameter to a new + // line. Give the current first parameter the indentation of the second + // parameter in the list. + var firstLeadingIndentation = GetDesiredLeadingIndentation( + generator, syntaxFacts, existingParameters[0], includeLeadingNewLine: false); + + editor.InsertParameter(declaration, insertionIndex, + parameterDeclaration.WithLeadingTrivia(firstLeadingIndentation)); + var nextParameter = existingParameters[insertionIndex]; + + editor.ReplaceNode( + nextParameter, + nextParameter.WithPrependedLeadingTrivia(generator.ElasticCarriageReturnLineFeed) + .WithAdditionalAnnotations(Formatter.Annotation)); + } } else { @@ -287,12 +322,12 @@ private int NonParamsParameterCount(IMethodSymbol method) // Because we're going to 'steal' the existing comma from that parameter, // ensure that the next parameter has a new-line added to it so that it will // still stay on a new line. - var nextParameter = existingParameters[argumentIndex]; + var nextParameter = existingParameters[insertionIndex]; var leadingIndentation = GetDesiredLeadingIndentation( - generator, syntaxFacts, existingParameters[argumentIndex], includeLeadingNewLine: false); + generator, syntaxFacts, existingParameters[insertionIndex], includeLeadingNewLine: false); parameterDeclaration = parameterDeclaration.WithPrependedLeadingTrivia(leadingIndentation); - editor.InsertParameter(declaration, argumentIndex, parameterDeclaration); + editor.InsertParameter(declaration, insertionIndex, parameterDeclaration); editor.ReplaceNode( nextParameter, nextParameter.WithPrependedLeadingTrivia(generator.ElasticCarriageReturnLineFeed) @@ -301,7 +336,7 @@ private int NonParamsParameterCount(IMethodSymbol method) } else { - editor.InsertParameter(declaration, argumentIndex, parameterDeclaration); + editor.InsertParameter(declaration, insertionIndex, parameterDeclaration); } } }