提交 620cecb4 编写于 作者: R Ravi Chande

Merge pull request #14289 from rchande/tupleSigHelp

Signature help improvements for C# tuples (renamed to work around jenkins issues)
上级 1ccb04ed
......@@ -310,6 +310,7 @@
<Compile Include="Organizing\OrganizeUsingsTests.cs" />
<Compile Include="Structure\BlockSyntaxStructureTests.cs" />
<Compile Include="Structure\SwitchStatementStructureTests.cs" />
<Compile Include="SignatureHelp\TupleConstructionSignatureHelpProviderTests.cs" />
<Compile Include="UseThrowExpression\UseThrowExpressionTests_FixAllTests.cs" />
<Compile Include="UseThrowExpression\UseThrowExpressionTests.cs" />
<Compile Include="Structure\AbstractCSharpSyntaxNodeStructureTests.cs" />
......
......@@ -514,5 +514,71 @@ public Derived() [|: base{$$|])
var expectedOrderedItems = new List<SignatureHelpTestItem>();
await TestAsync(markup, expectedOrderedItems);
}
[Fact, Trait(Traits.Feature, Traits.Features.SignatureHelp)]
public async Task TypingTupleDoesNotDismiss1()
{
var markup = @"
class D { public D(object o) {} }
class C : D
{
public C() [|: base(($$)
|]{}
}";
var expectedOrderedItems = new List<SignatureHelpTestItem>();
expectedOrderedItems.Add(new SignatureHelpTestItem("D(object o)", currentParameterIndex: 0));
await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true);
}
[Fact, Trait(Traits.Feature, Traits.Features.SignatureHelp)]
public async Task TypingTupleDoesNotDismiss2()
{
var markup = @"
class D { public D(object o) {} }
class C : D
{
public C() [|: base((1,$$) |]{}
}";
var expectedOrderedItems = new List<SignatureHelpTestItem>();
expectedOrderedItems.Add(new SignatureHelpTestItem("D(object o)", currentParameterIndex: 0));
await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true);
}
[Fact, Trait(Traits.Feature, Traits.Features.SignatureHelp)]
public async Task TypingTupleDoesNotDismiss3()
{
var markup = @"
class D { public D(object o) {} }
class C : D
{
public C() [|: base((1, ($$)
|]{}
}";
var expectedOrderedItems = new List<SignatureHelpTestItem>();
expectedOrderedItems.Add(new SignatureHelpTestItem("D(object o)", currentParameterIndex: 0));
await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true);
}
[Fact, Trait(Traits.Feature, Traits.Features.SignatureHelp)]
public async Task TypingTupleDoesNotDismiss4()
{
var markup = @"
class D { public D(object o) {} }
class C : D
{
public C() [|: base((1, (2,$$) |]{}
}";
var expectedOrderedItems = new List<SignatureHelpTestItem>();
expectedOrderedItems.Add(new SignatureHelpTestItem("D(object o)", currentParameterIndex: 0));
await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true);
}
}
}
......@@ -2022,5 +2022,77 @@ void M()
await TestAsync(markup, new[] { new SignatureHelpTestItem("void List<int>.Add(int item)") });
}
[Fact, Trait(Traits.Feature, Traits.Features.SignatureHelp)]
public async Task TypingTupleDoesNotDismiss1()
{
var markup = @"
class C
{
int Foo(object x)
{
[|Foo(($$)|];
}
}";
var expectedOrderedItems = new List<SignatureHelpTestItem>();
expectedOrderedItems.Add(new SignatureHelpTestItem("int C.Foo(object x)", currentParameterIndex: 0));
await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true);
}
[Fact, Trait(Traits.Feature, Traits.Features.SignatureHelp)]
public async Task TypingTupleDoesNotDismiss2()
{
var markup = @"
class C
{
int Foo(object x)
{
[|Foo((1,$$)|];
}
}";
var expectedOrderedItems = new List<SignatureHelpTestItem>();
expectedOrderedItems.Add(new SignatureHelpTestItem("int C.Foo(object x)", currentParameterIndex: 0));
await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true);
}
[Fact, Trait(Traits.Feature, Traits.Features.SignatureHelp)]
public async Task TypingTupleDoesNotDismiss3()
{
var markup = @"
class C
{
int Foo(object x)
{
[|Foo((1, ($$)|];
}
}";
var expectedOrderedItems = new List<SignatureHelpTestItem>();
expectedOrderedItems.Add(new SignatureHelpTestItem("int C.Foo(object x)", currentParameterIndex: 0));
await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true);
}
[Fact, Trait(Traits.Feature, Traits.Features.SignatureHelp)]
public async Task TypingTupleDoesNotDismiss4()
{
var markup = @"
class C
{
int Foo(object x)
{
[|Foo((1, (2,$$)|];
}
}";
var expectedOrderedItems = new List<SignatureHelpTestItem>();
expectedOrderedItems.Add(new SignatureHelpTestItem("int C.Foo(object x)", currentParameterIndex: 0));
await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true);
}
}
}
......@@ -568,5 +568,85 @@ void foo(C c)
await TestAsync(markup);
}
[Fact, Trait(Traits.Feature, Traits.Features.SignatureHelp)]
public async Task TypingTupleDoesNotDismiss1()
{
var markup = @"
class C
{
public C(object o) { }
public C M()
{
return [|new C(($$)
|]}
}";
var expectedOrderedItems = new List<SignatureHelpTestItem>();
expectedOrderedItems.Add(new SignatureHelpTestItem("C(object o)", currentParameterIndex: 0));
await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true);
}
[Fact, Trait(Traits.Feature, Traits.Features.SignatureHelp)]
public async Task TypingTupleDoesNotDismiss2()
{
var markup = @"
class C
{
public C(object o) { }
public C M()
{
return [|new C((1,$$)
|]}
}";
var expectedOrderedItems = new List<SignatureHelpTestItem>();
expectedOrderedItems.Add(new SignatureHelpTestItem("C(object o)", currentParameterIndex: 0));
await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true);
}
[Fact, Trait(Traits.Feature, Traits.Features.SignatureHelp)]
public async Task TypingTupleDoesNotDismiss3()
{
var markup = @"
class C
{
public C(object o) { }
public C M()
{
return [|new C((1, ($$)
|]}
}";
var expectedOrderedItems = new List<SignatureHelpTestItem>();
expectedOrderedItems.Add(new SignatureHelpTestItem("C(object o)", currentParameterIndex: 0));
await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true);
}
[Fact, Trait(Traits.Feature, Traits.Features.SignatureHelp)]
public async Task TypingTupleDoesNotDismiss4()
{
var markup = @"
class C
{
public C(object o) { }
public C M()
{
return [|new C((1, (2,$$)
|]}
}";
var expectedOrderedItems = new List<SignatureHelpTestItem>();
expectedOrderedItems.Add(new SignatureHelpTestItem("C(object o)", currentParameterIndex: 0));
await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true);
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.SignatureHelp;
using Microsoft.CodeAnalysis.CSharp.SignatureHelp;
using Xunit;
using Microsoft.CodeAnalysis.Editor.UnitTests.SignatureHelp;
using Roslyn.Test.Utilities;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.SignatureHelp
{
public class TupleConstructionSignatureHelpProviderTests : AbstractCSharpSignatureHelpProviderTests
{
public TupleConstructionSignatureHelpProviderTests(CSharpTestWorkspaceFixture workspaceFixture) : base(workspaceFixture)
{
}
internal override ISignatureHelpProvider CreateSignatureHelpProvider()
{
return new TupleConstructionSignatureHelpProvider();
}
[Fact, Trait(Traits.Feature, Traits.Features.SignatureHelp)]
public async Task InvocationAfterOpenParen()
{
var markup = @"
class C
{
(int, int) y = [|($$
|]}";
var expectedOrderedItems = new List<SignatureHelpTestItem>();
expectedOrderedItems.Add(new SignatureHelpTestItem("(int, int)", currentParameterIndex: 0));
await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true);
}
[Fact, Trait(Traits.Feature, Traits.Features.SignatureHelp)]
public async Task InvocationAfterOpenParen2()
{
var markup = @"
class C
{
(int, int) y = [|($$)|]
}";
var expectedOrderedItems = new List<SignatureHelpTestItem>();
expectedOrderedItems.Add(new SignatureHelpTestItem("(int, int)", currentParameterIndex: 0));
await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true);
}
[Fact, Trait(Traits.Feature, Traits.Features.SignatureHelp)]
public async Task InvocationAfterComma1()
{
var markup = @"
class C
{
(int, int) y = [|(1,$$
|]}";
var expectedOrderedItems = new List<SignatureHelpTestItem>();
expectedOrderedItems.Add(new SignatureHelpTestItem("(int, int)", currentParameterIndex: 1));
await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true);
}
[Fact, Trait(Traits.Feature, Traits.Features.SignatureHelp)]
public async Task InvocationAfterComma2()
{
var markup = @"
class C
{
(int, int) y = [|(1,$$)|]
}";
var expectedOrderedItems = new List<SignatureHelpTestItem>();
expectedOrderedItems.Add(new SignatureHelpTestItem("(int, int)", currentParameterIndex: 1));
await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true);
}
[Fact, Trait(Traits.Feature, Traits.Features.SignatureHelp)]
public async Task ParameterIndexWithNameTyped()
{
var markup = @"
class C
{
(int a, int b) y = [|(b: $$
|]}";
var expectedOrderedItems = new List<SignatureHelpTestItem>();
// currentParameterIndex only considers the position in the argument list
// and not names, hence passing 0 even though the controller will highlight
// "int b" in the actual display
expectedOrderedItems.Add(new SignatureHelpTestItem("(int a, int b)", currentParameterIndex: 0));
await TestAsync(markup, expectedOrderedItems);
}
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/14277"), Trait(Traits.Feature, Traits.Features.SignatureHelp)]
public async Task NestedTuple()
{
var markup = @"
class C
{
(int a, (int b, int c)) y = [|(1, ($$
|]}";
var expectedOrderedItems = new List<SignatureHelpTestItem>();
expectedOrderedItems.Add(new SignatureHelpTestItem("(int b, int c)", currentParameterIndex: 0));
await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true);
}
[Fact, Trait(Traits.Feature, Traits.Features.SignatureHelp)]
public async Task NestedTupleWhenNotInferred()
{
var markup = @"
class C
{
(int, object) y = [|(1, ($$
|]}";
var expectedOrderedItems = new List<SignatureHelpTestItem>();
expectedOrderedItems.Add(new SignatureHelpTestItem("(int, object)", currentParameterIndex: 1));
await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true);
}
[Fact, Trait(Traits.Feature, Traits.Features.SignatureHelp)]
public async Task NestedTupleWhenNotInferred2()
{
var markup = @"
class C
{
(int, object) y = [|(1, (2,$$
|]}";
var expectedOrderedItems = new List<SignatureHelpTestItem>();
expectedOrderedItems.Add(new SignatureHelpTestItem("(int, object)", currentParameterIndex: 1));
await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true);
}
[Fact, Trait(Traits.Feature, Traits.Features.SignatureHelp)]
public async Task NestedTupleWhenNotInferred3()
{
var markup = @"
class C
{
(int, object) y = [|(1, ($$
|]}";
var expectedOrderedItems = new List<SignatureHelpTestItem>();
expectedOrderedItems.Add(new SignatureHelpTestItem("(int, object)", currentParameterIndex: 1));
await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true);
}
[Fact, Trait(Traits.Feature, Traits.Features.SignatureHelp)]
public async Task NestedTupleWhenNotInferred4()
{
var markup = @"
class C
{
(object, object) y = [|(($$
|]}";
var expectedOrderedItems = new List<SignatureHelpTestItem>();
expectedOrderedItems.Add(new SignatureHelpTestItem("(object, object)", currentParameterIndex: 0));
await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true);
}
[Fact, Trait(Traits.Feature, Traits.Features.SignatureHelp)]
public async Task MultipleOverloads()
{
var markup = @"
class Program
{
static void Main(string[] args)
{
Do1([|($$)|])
}
static void Do1((int, int) i) { }
static void Do1((string, string) s) { }
}";
var expectedOrderedItems = new List<SignatureHelpTestItem>();
expectedOrderedItems.Add(new SignatureHelpTestItem("(int, int)", currentParameterIndex: 0));
expectedOrderedItems.Add(new SignatureHelpTestItem("(string, string)", currentParameterIndex: 0));
await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true);
}
}
}
......@@ -46,7 +46,7 @@ internal static class DefaultSignatureHelpSelector
private static int GetSelectedParameter(SignatureHelpItem bestItem, int parameterIndex, string parameterName, bool isCaseSensitive)
{
if (parameterName != null)
if (!string.IsNullOrEmpty(parameterName))
{
var comparer = isCaseSensitive ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase;
var index = bestItem.Parameters.IndexOf(p => comparer.Equals(p.Name, parameterName));
......
......@@ -477,5 +477,42 @@ class C
Await state.AssertSignatureHelpSession()
End Using
End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.SignatureHelp)>
Public Async Function MixedTupleNaming() As Task
Using state = TestState.CreateCSharpTestState(
<Document>
class C
{
void Foo()
{
(int, int x) t = (5$$
}
}
</Document>)
state.SendTypeChars(",")
Await state.AssertSelectedSignatureHelpItem(displayText:="(int, int x)", selectedParameter:="int x")
End Using
End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.SignatureHelp)>
Public Async Function ParameterSelectionWhileParsedAsParenthesizedExpression() As Task
Using state = TestState.CreateCSharpTestState(
<Document>
class C
{
void Foo()
{
(int a, string b) x = (b$$
}
}
</Document>)
state.SendInvokeSignatureHelp()
Await state.AssertSelectedSignatureHelpItem(displayText:="(int a, string b)", selectedParameter:="int a")
End Using
End Function
End Class
End Namespace
......@@ -45,10 +45,10 @@ public static Type[] GetLanguageNeutralTypes()
typeof(TestExtensionErrorHandler)
};
return types.Concat(TestHelpers.GetAllTypesWithStaticFieldsImplementingType(typeof(InternalSolutionCrawlerOptions).Assembly, typeof(Microsoft.CodeAnalysis.Options.IOption)))
.Concat(TestHelpers.GetAllTypesWithStaticFieldsImplementingType(typeof(EditorComponentOnOffOptions).Assembly, typeof(Microsoft.CodeAnalysis.Options.IOption)))
.Concat(TestHelpers.GetAllTypesWithStaticFieldsImplementingType(typeof(ServiceComponentOnOffOptions).Assembly, typeof(Microsoft.CodeAnalysis.Options.IOption)))
.Concat(TestHelpers.GetAllTypesWithStaticFieldsImplementingType(typeof(Microsoft.CodeAnalysis.Formatting.FormattingOptions).Assembly, typeof(Microsoft.CodeAnalysis.Options.IOption)))
return types//.Concat(TestHelpers.GetAllTypesWithStaticFieldsImplementingType(typeof(InternalSolutionCrawlerOptions).Assembly, typeof(Microsoft.CodeAnalysis.Options.IOption)))
//.Concat(TestHelpers.GetAllTypesWithStaticFieldsImplementingType(typeof(EditorComponentOnOffOptions).Assembly, typeof(Microsoft.CodeAnalysis.Options.IOption)))
//.Concat(TestHelpers.GetAllTypesWithStaticFieldsImplementingType(typeof(ServiceComponentOnOffOptions).Assembly, typeof(Microsoft.CodeAnalysis.Options.IOption)))
//.Concat(TestHelpers.GetAllTypesWithStaticFieldsImplementingType(typeof(Microsoft.CodeAnalysis.Formatting.FormattingOptions).Assembly, typeof(Microsoft.CodeAnalysis.Options.IOption)))
.Distinct()
.ToArray();
}
......
......@@ -363,6 +363,7 @@
<Compile Include="InlineDeclaration\CSharpInlineDeclarationCodeFixProvider.cs" />
<Compile Include="InlineDeclaration\CSharpInlineDeclarationDiagnosticAnalyzer.cs" />
<Compile Include="UseObjectInitializer\CSharpUseObjectInitializerCodeFixProvider.cs" />
<Compile Include="SignatureHelp\TupleConstructionSignatureHelpProvider.cs" />
<Compile Include="UseThrowExpression\CSharpUseThrowExpressionDiagnosticAnalyzer.cs" />
<Compile Include="Structure\CSharpBlockStructureProvider.cs" />
<Compile Include="Structure\CSharpStructureHelpers.cs" />
......
......@@ -43,11 +43,7 @@ private bool TryGetConstructorInitializer(SyntaxNode root, int position, ISyntax
private bool IsTriggerToken(SyntaxToken token)
{
return !token.IsKind(SyntaxKind.None) &&
token.ValueText.Length == 1 &&
IsTriggerCharacter(token.ValueText[0]) &&
token.Parent is ArgumentListSyntax &&
token.Parent.Parent is ConstructorInitializerSyntax;
return SignatureHelpUtilities.IsTriggerParenOrComma<ConstructorInitializerSyntax>(token, IsTriggerCharacter);
}
private static bool IsArgumentListToken(ConstructorInitializerSyntax expression, SyntaxToken token)
......
......@@ -42,11 +42,7 @@ private bool TryGetInvocationExpression(SyntaxNode root, int position, ISyntaxFa
private bool IsTriggerToken(SyntaxToken token)
{
return !token.IsKind(SyntaxKind.None) &&
token.ValueText.Length == 1 &&
IsTriggerCharacter(token.ValueText[0]) &&
token.Parent is ArgumentListSyntax &&
token.Parent.Parent is InvocationExpressionSyntax;
return SignatureHelpUtilities.IsTriggerParenOrComma<InvocationExpressionSyntax>(token, IsTriggerCharacter);
}
private static bool IsArgumentListToken(InvocationExpressionSyntax expression, SyntaxToken token)
......
......@@ -10,6 +10,7 @@
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.SignatureHelp;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.CSharp.Extensions;
namespace Microsoft.CodeAnalysis.CSharp.SignatureHelp
{
......@@ -38,11 +39,7 @@ private bool TryGetObjectCreationExpression(SyntaxNode root, int position, ISynt
private bool IsTriggerToken(SyntaxToken token)
{
return !token.IsKind(SyntaxKind.None) &&
token.ValueText.Length == 1 &&
IsTriggerCharacter(token.ValueText[0]) &&
token.Parent is ArgumentListSyntax &&
token.Parent.Parent is ObjectCreationExpressionSyntax;
return SignatureHelpUtilities.IsTriggerParenOrComma<ObjectCreationExpressionSyntax>(token, IsTriggerCharacter);
}
private static bool IsArgumentListToken(ObjectCreationExpressionSyntax expression, SyntaxToken token)
......
......@@ -7,6 +7,7 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.SignatureHelp;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.Shared.Extensions;
namespace Microsoft.CodeAnalysis.CSharp.SignatureHelp
{
......@@ -83,5 +84,47 @@ internal static TextSpan GetSignatureHelpSpan(AttributeArgumentListSyntax argume
{
return CommonSignatureHelpUtilities.GetSignatureHelpSpan(argumentList, s_getAttributeArgumentListCloseToken);
}
internal static bool IsTriggerParenOrComma<TSyntaxNode>(SyntaxToken token, Func<char, bool> isTriggerCharacter) where TSyntaxNode : SyntaxNode
{
// Don't dismiss if the user types ( to start a parenthesized expression or tuple
// Note that the tuple initially parses as a parenthesized expression
if (token.IsKind(SyntaxKind.OpenParenToken) && token.Parent.IsKind(SyntaxKind.ParenthesizedExpression))
{
var parenthesizedExpr = ((ParenthesizedExpressionSyntax)token.Parent).WalkUpParentheses();
if (parenthesizedExpr.Parent is ArgumentSyntax)
{
var parent = parenthesizedExpr.Parent;
var grandParent = parent.Parent;
if (grandParent is ArgumentListSyntax && grandParent.Parent is TSyntaxNode)
{
// Argument to TSyntaxNode's argument list
return true;
}
else
{
// Argument to a tuple in TSyntaxNode's argument list
return grandParent is TupleExpressionSyntax && parenthesizedExpr.GetAncestor<TSyntaxNode>() != null;
}
}
else
{
// Not an argument
return false;
}
}
// Don't dismiss if the user types ',' to add a member to a tuple
if (token.IsKind(SyntaxKind.CommaToken) && token.Parent is TupleExpressionSyntax && token.GetAncestor<TSyntaxNode>() != null)
{
return true;
}
return !token.IsKind(SyntaxKind.None) &&
token.ValueText.Length == 1 &&
isTriggerCharacter(token.ValueText[0]) &&
token.Parent is ArgumentListSyntax &&
token.Parent.Parent is TSyntaxNode;
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.SignatureHelp;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Roslyn.Utilities;
using System.Collections.Immutable;
namespace Microsoft.CodeAnalysis.CSharp.SignatureHelp
{
[ExportSignatureHelpProvider("TupleSignatureHelpProvider", LanguageNames.CSharp), Shared]
internal class TupleConstructionSignatureHelpProvider : AbstractCSharpSignatureHelpProvider
{
private static readonly Func<TupleExpressionSyntax, SyntaxToken> s_getOpenToken = e => e.OpenParenToken;
private static readonly Func<TupleExpressionSyntax, SyntaxToken> s_getCloseToken = e => e.CloseParenToken;
private static readonly Func<TupleExpressionSyntax, IEnumerable<SyntaxNodeOrToken>> s_getArgumentsWithSeparators = e => e.Arguments.GetWithSeparators();
private static readonly Func<TupleExpressionSyntax, IEnumerable<string>> s_getArgumentNames = e => e.Arguments.Select(a => a.NameColon?.Name.Identifier.ValueText ?? string.Empty);
public override SignatureHelpState GetCurrentArgumentState(SyntaxNode root, int position, ISyntaxFactsService syntaxFacts, TextSpan currentSpan, CancellationToken cancellationToken)
{
TupleExpressionSyntax expression;
if (GetOuterMostTupleExpressionInSpan(root, position, syntaxFacts, currentSpan, cancellationToken, out expression))
{
return CommonSignatureHelpUtilities.GetSignatureHelpState(expression, position,
getOpenToken: s_getOpenToken,
getCloseToken: s_getCloseToken,
getArgumentsWithSeparators: s_getArgumentsWithSeparators,
getArgumentNames: s_getArgumentNames);
}
ParenthesizedExpressionSyntax parenthesizedExpression = null;
if (GetOuterMostParenthesizedExpressionInSpan(root, position, syntaxFacts, currentSpan, cancellationToken, out parenthesizedExpression))
{
if (currentSpan.Start == parenthesizedExpression.SpanStart)
{
return new SignatureHelpState(
argumentIndex: 0,
argumentCount: 0,
argumentName: string.Empty,
argumentNames: null);
}
}
return null;
}
private bool GetOuterMostTupleExpressionInSpan(SyntaxNode root, int position,
ISyntaxFactsService syntaxFacts, TextSpan currentSpan, CancellationToken cancellationToken, out TupleExpressionSyntax result)
{
result = null;
TupleExpressionSyntax expression;
while (TryGetTupleExpression(SignatureHelpTriggerReason.InvokeSignatureHelpCommand,
root, position, syntaxFacts, cancellationToken, out expression))
{
if (!currentSpan.Contains(expression.Span))
{
break;
}
result = expression;
position = expression.SpanStart;
}
return result != null;
}
private bool GetOuterMostParenthesizedExpressionInSpan(SyntaxNode root, int position,
ISyntaxFactsService syntaxFacts, TextSpan currentSpan, CancellationToken cancellationToken, out ParenthesizedExpressionSyntax result)
{
result = null;
ParenthesizedExpressionSyntax expression;
while (TryGetParenthesizedExpression(SignatureHelpTriggerReason.InvokeSignatureHelpCommand,
root, position, syntaxFacts, cancellationToken, out expression))
{
if (!currentSpan.Contains(expression.Span))
{
break;
}
result = expression;
position = expression.SpanStart;
}
return result != null;
}
public override Boolean IsRetriggerCharacter(Char ch)
{
return ch == ')';
}
public override Boolean IsTriggerCharacter(Char ch)
{
return ch == '(' || ch == ',';
}
protected override async Task<SignatureHelpItems> GetItemsWorkerAsync(Document document, int position, SignatureHelpTriggerInfo triggerInfo, CancellationToken cancellationToken)
{
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var syntaxFacts = document.Project.LanguageServices.GetService<ISyntaxFactsService>();
var typeInferrer = document.Project.LanguageServices.GetService<ITypeInferenceService>();
ExpressionSyntax targetExpression;
var inferredTypes = FindNearestTupleConstructionWithInferrableType(root, semanticModel, position, triggerInfo,
typeInferrer, syntaxFacts, cancellationToken, out targetExpression);
if (inferredTypes == null || !inferredTypes.Any())
{
return null;
}
return CreateItems(position, root, syntaxFacts, targetExpression, semanticModel, inferredTypes, cancellationToken);
}
IEnumerable<INamedTypeSymbol> FindNearestTupleConstructionWithInferrableType(SyntaxNode root, SemanticModel semanticModel, int position, SignatureHelpTriggerInfo triggerInfo,
ITypeInferenceService typeInferrer, ISyntaxFactsService syntaxFacts, CancellationToken cancellationToken, out ExpressionSyntax targetExpression)
{
// Walk upward through TupleExpressionSyntax/ParenthsizedExpressionSyntax looking for a
// place where we can infer a tuple type.
TupleExpressionSyntax tupleExpression = null;
ParenthesizedExpressionSyntax parenthesizedExpression = null;
while (TryGetTupleExpression(triggerInfo.TriggerReason, root, position, syntaxFacts, cancellationToken, out tupleExpression) ||
TryGetParenthesizedExpression(triggerInfo.TriggerReason, root, position, syntaxFacts, cancellationToken, out parenthesizedExpression))
{
targetExpression = (ExpressionSyntax)tupleExpression ?? parenthesizedExpression;
var inferredTypes = typeInferrer.InferTypes(semanticModel, targetExpression.SpanStart, cancellationToken);
var tupleTypes = inferredTypes.Where(t => t.IsTupleType).OfType<INamedTypeSymbol>().ToList();
if (tupleTypes.Any())
{
return tupleTypes;
}
position = targetExpression.GetFirstToken().SpanStart;
}
targetExpression = null;
return null;
}
private SignatureHelpItems CreateItems(int position, SyntaxNode root, ISyntaxFactsService syntaxFacts,
SyntaxNode targetExpression, SemanticModel semanticModel, IEnumerable<INamedTypeSymbol> tupleTypes, CancellationToken cancellationToken)
{
var prefixParts = SpecializedCollections.SingletonEnumerable(new SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, null, "("));
var suffixParts = SpecializedCollections.SingletonEnumerable(new SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, null, ")"));
var separatorParts = GetSeparatorParts();
var items = tupleTypes.Select(t =>
new SignatureHelpItem(isVariadic: false,
documentationFactory: null,
prefixParts: prefixParts,
separatorParts: separatorParts,
suffixParts: suffixParts,
parameters: ConvertTupleMembers(t, semanticModel, position),
descriptionParts: null)).ToList();
var state = GetCurrentArgumentState(root, position, syntaxFacts, targetExpression.FullSpan, cancellationToken);
return CreateSignatureHelpItems(items, targetExpression.Span, state);
}
private IEnumerable<SignatureHelpParameter> ConvertTupleMembers(INamedTypeSymbol tupleType, SemanticModel semanticModel, int position)
{
var spacePart = Space();
var result = new List<SignatureHelpParameter>();
for (int i = 0; i < tupleType.TupleElementTypes.Length; i++)
{
var type = tupleType.TupleElementTypes[i];
var elementName = GetElementName(tupleType.TupleElementNames, i);
var typeParts = type.ToMinimalDisplayParts(semanticModel, position).ToList();
if (!string.IsNullOrEmpty(elementName))
{
typeParts.Add(spacePart);
typeParts.Add(new SymbolDisplayPart(SymbolDisplayPartKind.PropertyName, null, elementName));
}
result.Add(new SignatureHelpParameter(name: string.Empty, isOptional: false, documentationFactory: null, displayParts: typeParts));
}
return result;
}
// The display name for each parameter. Empty strings are allowed for
// parameters without names.
private string GetElementName(ImmutableArray<string> tupleElementNames, int i)
{
if (tupleElementNames == default(ImmutableArray<string>))
{
return string.Empty;
}
return tupleElementNames[i] ?? string.Empty;
}
private bool TryGetTupleExpression(SignatureHelpTriggerReason triggerReason, SyntaxNode root, int position,
ISyntaxFactsService syntaxFacts, CancellationToken cancellationToken, out TupleExpressionSyntax tupleExpression)
{
return CommonSignatureHelpUtilities.TryGetSyntax(root, position, syntaxFacts, triggerReason, IsTupleExpressionTriggerToken,
IsTupleArgumentListToken, cancellationToken, out tupleExpression);
}
private bool IsTupleExpressionTriggerToken(SyntaxToken token)
{
return SignatureHelpUtilities.IsTriggerParenOrComma<TupleExpressionSyntax>(token, IsTriggerCharacter);
}
private static bool IsTupleArgumentListToken(TupleExpressionSyntax tupleExpression, SyntaxToken token)
{
return tupleExpression.Arguments.FullSpan.Contains(token.SpanStart) &&
token != tupleExpression.CloseParenToken;
}
private bool TryGetParenthesizedExpression(SignatureHelpTriggerReason triggerReason, SyntaxNode root, int position,
ISyntaxFactsService syntaxFacts, CancellationToken cancellationToken, out ParenthesizedExpressionSyntax parenthesizedExpression)
{
return CommonSignatureHelpUtilities.TryGetSyntax(root, position, syntaxFacts, triggerReason,
IsParenthesizedExpressionTriggerToken, IsParenthesizedExpressionToken, cancellationToken, out parenthesizedExpression);
}
private bool IsParenthesizedExpressionTriggerToken(SyntaxToken token)
{
return token.IsKind(SyntaxKind.OpenParenToken) && token.Parent is ParenthesizedExpressionSyntax;
}
private static bool IsParenthesizedExpressionToken(ParenthesizedExpressionSyntax expr, SyntaxToken token)
{
return expr.FullSpan.Contains(token.SpanStart) &&
token != expr.CloseParenToken;
}
}
}
......@@ -69,7 +69,9 @@ internal class SignatureHelpItem
IEnumerable<SignatureHelpParameter> parameters,
IEnumerable<SymbolDisplayPart> descriptionParts)
: this(isVariadic,
c => documentationFactory(c).ToTaggedText(),
documentationFactory != null
? c => documentationFactory(c).ToTaggedText()
: s_emptyDocumentationFactory,
prefixParts.ToTaggedText(),
separatorParts.ToTaggedText(),
suffixParts.ToTaggedText(),
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册