提交 c412c344 编写于 作者: C CyrusNajmabadi 提交者: GitHub

Merge pull request #18912 from CyrusNajmabadi/addNamedArgLocation

Only offer the 'add named parameter' refactoring when on the start of the argument being passed in.
......@@ -199,5 +199,134 @@ class C : System.Attribute { public C(int arg1) {} public int P { get; set; } }"
@"[C(arg1: 1, P = 2)]
class C : System.Attribute { public C(int arg1) {} public int P { get; set; } }");
}
[WorkItem(18848, "https://github.com/dotnet/roslyn/issues/18848")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)]
public async Task TestAvailableOnFirstTokenOfArgument1()
{
await TestInRegularAndScriptAsync(
@"class C
{
void M(int arg1, int arg2)
=> M([||]1 + 2, 2);
}",
@"class C
{
void M(int arg1, int arg2)
=> M(arg1: 1 + 2, arg2: 2);
}");
}
[WorkItem(18848, "https://github.com/dotnet/roslyn/issues/18848")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)]
public async Task TestAvailableOnFirstTokenOfArgument2()
{
await TestInRegularAndScriptAsync(
@"class C
{
void M(int arg1, int arg2)
=> M(1[||] + 2, 2);
}",
@"class C
{
void M(int arg1, int arg2)
=> M(arg1: 1 + 2, arg2: 2);
}");
}
[WorkItem(18848, "https://github.com/dotnet/roslyn/issues/18848")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)]
public async Task TestNotMissingWhenInsideSingleLineArgument1()
{
await TestInRegularAndScriptAsync(
@"
using System;
class C
{
void M(Action arg1, int arg2)
=> M([||]() => { }, 2);
}",
@"
using System;
class C
{
void M(Action arg1, int arg2)
=> M(arg1: () => { }, arg2: 2);
}");
}
[WorkItem(18848, "https://github.com/dotnet/roslyn/issues/18848")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)]
public async Task TestNotMissingWhenInsideSingleLineArgument2()
{
await TestInRegularAndScript1Async(
@"class C
{
void M(int arg1, int arg2)
=> M(1 [||]+ 2, 2);
}",
@"class C
{
void M(int arg1, int arg2)
=> M(arg1: 1 + 2, arg2: 2);
}");
}
[WorkItem(18848, "https://github.com/dotnet/roslyn/issues/18848")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)]
public async Task TestNotMissingWhenInsideSingleLineArgument3()
{
await TestInRegularAndScriptAsync(
@"
using System;
class C
{
void M(Action arg1, int arg2)
=> M(() => { [||] }, 2);
}",
@"
using System;
class C
{
void M(Action arg1, int arg2)
=> M(arg1: () => { }, arg2: 2);
}");
}
[WorkItem(18848, "https://github.com/dotnet/roslyn/issues/18848")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)]
public async Task TestMissingNotOnStartingLineOfArgument1()
{
await TestMissingAsync(
@"
using System;
class C
{
void M(Action arg1, int arg2)
=> M(() => {
[||]
}, 2);
}");
}
[WorkItem(18848, "https://github.com/dotnet/roslyn/issues/18848")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)]
public async Task TestMissingWithSelection()
{
await TestMissingAsync(
@"
using System;
class C
{
void M(Action arg1, int arg2)
=> M([|1 + 2|], 3);
}");
}
}
}
\ No newline at end of file
......@@ -19,17 +19,22 @@ protected interface IAnalyzer
CodeRefactoringContext context, SyntaxNode root, CancellationToken cancellationToken);
}
protected abstract class Analyzer<TBaseSyntax, TSyntax, TListSyntax> : IAnalyzer
where TBaseSyntax : SyntaxNode
where TSyntax : TBaseSyntax
where TListSyntax : SyntaxNode
protected abstract class Analyzer<TBaseArgumentSyntax, TArgumentSyntax, TArgumentListSyntax> : IAnalyzer
where TBaseArgumentSyntax : SyntaxNode
where TArgumentSyntax : TBaseArgumentSyntax
where TArgumentListSyntax : SyntaxNode
{
public async Task ComputeRefactoringsAsync(
CodeRefactoringContext context, SyntaxNode root, CancellationToken cancellationToken)
{
var document = context.Document;
var argument = root.FindNode(context.Span).FirstAncestorOrSelf<TBaseSyntax>() as TSyntax;
if (context.Span.Length > 0)
{
return;
}
var argument = root.FindNode(context.Span).FirstAncestorOrSelf<TBaseArgumentSyntax>() as TArgumentSyntax;
if (argument == null)
{
return;
......@@ -40,6 +45,19 @@ protected abstract class Analyzer<TBaseSyntax, TSyntax, TListSyntax> : IAnalyzer
return;
}
// Arguments can be arbitrarily large. Only offer this feature if the caret is on hte
// line that the argument starts on.
var sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
var argumentStartLine = sourceText.Lines.GetLineFromPosition(argument.Span.Start).LineNumber;
var caretLine = sourceText.Lines.GetLineFromPosition(context.Span.Start).LineNumber;
if (argumentStartLine != caretLine)
{
return;
}
var receiver = GetReceiver(argument);
if (receiver == null)
{
......@@ -65,13 +83,13 @@ protected abstract class Analyzer<TBaseSyntax, TSyntax, TListSyntax> : IAnalyzer
return;
}
var argumentList = (TListSyntax)argument.Parent;
var argumentList = (TArgumentListSyntax)argument.Parent;
var (index, count) = GetArgumentListIndexAndCount(argument, argumentList);
var arguments = GetArguments(argumentList);
for (var i = index; i < count; i++)
{
if (!(arguments[i] is TSyntax))
if (!(arguments[i] is TArgumentSyntax))
{
return;
}
......@@ -92,41 +110,41 @@ protected abstract class Analyzer<TBaseSyntax, TSyntax, TListSyntax> : IAnalyzer
private Task<Document> AddNamedArgumentsAsync(
SyntaxNode root,
Document document,
TSyntax firstArgument,
TArgumentSyntax firstArgument,
ImmutableArray<IParameterSymbol> parameters,
int index)
{
var argumentList = (TListSyntax)firstArgument.Parent;
var argumentList = (TArgumentListSyntax)firstArgument.Parent;
var newArgumentList = GetOrSynthesizeNamedArguments(parameters, argumentList, index);
var newRoot = root.ReplaceNode(argumentList, newArgumentList);
return Task.FromResult(document.WithSyntaxRoot(newRoot));
}
private (int, int) GetArgumentListIndexAndCount(TSyntax argument, TListSyntax argumentList)
private (int, int) GetArgumentListIndexAndCount(TArgumentSyntax argument, TArgumentListSyntax argumentList)
{
var arguments = GetArguments(argumentList);
return (arguments.IndexOf(argument), arguments.Count);
}
private TListSyntax GetOrSynthesizeNamedArguments(
ImmutableArray<IParameterSymbol> parameters, TListSyntax argumentList, int index)
private TArgumentListSyntax GetOrSynthesizeNamedArguments(
ImmutableArray<IParameterSymbol> parameters, TArgumentListSyntax argumentList, int index)
{
var arguments = GetArguments(argumentList);
var namedArguments = arguments
.Select((argument, i) => i >= index && argument is TSyntax s && IsPositionalArgument(s)
.Select((argument, i) => i >= index && argument is TArgumentSyntax s && IsPositionalArgument(s)
? WithName(s, parameters[i].Name).WithTriviaFrom(argument)
: argument);
return WithArguments(argumentList, namedArguments, arguments.GetSeparators());
}
protected abstract TListSyntax WithArguments(
TListSyntax argumentList, IEnumerable<TBaseSyntax> namedArguments, IEnumerable<SyntaxToken> separators);
protected abstract TArgumentListSyntax WithArguments(
TArgumentListSyntax argumentList, IEnumerable<TBaseArgumentSyntax> namedArguments, IEnumerable<SyntaxToken> separators);
protected abstract bool IsLegalToAddNamedArguments(ImmutableArray<IParameterSymbol> parameters, int argumentCount);
protected abstract TSyntax WithName(TSyntax argument, string name);
protected abstract bool IsPositionalArgument(TSyntax argument);
protected abstract SeparatedSyntaxList<TBaseSyntax> GetArguments(TListSyntax argumentList);
protected abstract TArgumentSyntax WithName(TArgumentSyntax argument, string name);
protected abstract bool IsPositionalArgument(TArgumentSyntax argument);
protected abstract SeparatedSyntaxList<TBaseArgumentSyntax> GetArguments(TArgumentListSyntax argumentList);
protected abstract SyntaxNode GetReceiver(SyntaxNode argument);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册