提交 4a475f6e 编写于 作者: B Brett Forsgren

show conditional element access sig-help after arbitrary expressions

the old model would only appear after an identifier name
上级 8f393ba5
......@@ -39,7 +39,8 @@ public override bool IsRetriggerCharacter(char ch)
private static bool TryGetElementAccessExpression(SyntaxNode root, int position, ISyntaxFactsService syntaxFacts, SignatureHelpTriggerReason triggerReason, CancellationToken cancellationToken, out ExpressionSyntax identifier, out SyntaxToken openBrace)
{
return CompleteElementAccessExpression.TryGetSyntax(root, position, syntaxFacts, triggerReason, cancellationToken, out identifier, out openBrace) ||
IncompleteElementAccessExpression.TryGetSyntax(root, position, syntaxFacts, triggerReason, cancellationToken, out identifier, out openBrace);
IncompleteElementAccessExpression.TryGetSyntax(root, position, syntaxFacts, triggerReason, cancellationToken, out identifier, out openBrace) ||
ConditionalAccessExpression.TryGetSyntax(root, position, syntaxFacts, triggerReason, cancellationToken, out identifier, out openBrace);
}
protected override async Task<SignatureHelpItems> GetItemsWorkerAsync(Document document, int position, SignatureHelpTriggerInfo triggerInfo, CancellationToken cancellationToken)
......@@ -115,7 +116,15 @@ private TextSpan GetTextSpan(ExpressionSyntax expression, SyntaxToken openBracke
{
if (openBracket.Parent is BracketedArgumentListSyntax)
{
return CompleteElementAccessExpression.GetTextSpan(expression, openBracket);
var conditional = expression.Parent as ConditionalAccessExpressionSyntax;
if (conditional != null)
{
return TextSpan.FromBounds(conditional.Span.Start, openBracket.FullSpan.End);
}
else
{
return CompleteElementAccessExpression.GetTextSpan(expression, openBracket);
}
}
else if (openBracket.Parent is ArrayRankSpecifierSyntax)
{
......@@ -142,16 +151,35 @@ public override SignatureHelpState GetCurrentArgumentState(SyntaxNode root, int
return null;
}
ElementAccessExpressionSyntax elementAccessExpression = SyntaxFactory.ElementAccessExpression(
expression,
SyntaxFactory.ParseBracketedArgumentList(openBracket.Parent.ToString()));
// If the user is actively typing, it's likely that we're in a broken state and the
// syntax tree will be incorrect. Because of this we need to synthesize a new
// bracketed argument list so we can correctly map the cursor to the current argument
// and then we need to account for this and offset the position check accordingly.
int offset;
BracketedArgumentListSyntax argumentList;
var newBracketedArgumentList = SyntaxFactory.ParseBracketedArgumentList(openBracket.Parent.ToString());
if (expression.Parent is ConditionalAccessExpressionSyntax)
{
// The typed code looks like: <expression>?[
var conditional = (ConditionalAccessExpressionSyntax)expression.Parent;
var elementBinding = SyntaxFactory.ElementBindingExpression(newBracketedArgumentList);
var conditionalAccessExpression = SyntaxFactory.ConditionalAccessExpression(expression, elementBinding);
offset = expression.SpanStart - conditionalAccessExpression.SpanStart;
argumentList = ((ElementBindingExpressionSyntax)conditionalAccessExpression.WhenNotNull).ArgumentList;
}
else
{
// The typed code looks like:
// <expression>[
// or
// <identifier>?[
ElementAccessExpressionSyntax elementAccessExpression = SyntaxFactory.ElementAccessExpression(expression, newBracketedArgumentList);
offset = expression.SpanStart - elementAccessExpression.SpanStart;
argumentList = elementAccessExpression.ArgumentList;
}
// Because we synthesized the elementAccessExpression, it will have an index starting at 0
// instead of at the actual position it's at in the text. Because of this, we need to
// offset the position we are checking accordingly.
var offset = expression.SpanStart - elementAccessExpression.SpanStart;
position -= offset;
return SignatureHelpUtilities.GetSignatureHelpState(elementAccessExpression.ArgumentList, position);
return SignatureHelpUtilities.GetSignatureHelpState(argumentList, position);
}
private bool TryGetComIndexers(SemanticModel semanticModel, ExpressionSyntax expression, CancellationToken cancellationToken, out IEnumerable<IPropertySymbol> indexers, out ITypeSymbol expressionType)
......@@ -254,7 +282,8 @@ internal static bool IsArgumentListToken(ElementAccessExpressionSyntax expressio
internal static TextSpan GetTextSpan(SyntaxNode expression, SyntaxToken openBracket)
{
Contract.ThrowIfFalse(openBracket.Parent is BracketedArgumentListSyntax && openBracket.Parent.Parent is ElementAccessExpressionSyntax);
Contract.ThrowIfFalse(openBracket.Parent is BracketedArgumentListSyntax &&
(openBracket.Parent.Parent is ElementAccessExpressionSyntax || openBracket.Parent.Parent is ElementBindingExpressionSyntax));
return SignatureHelpUtilities.GetSignatureHelpSpan((BracketedArgumentListSyntax)openBracket.Parent);
}
......@@ -275,7 +304,7 @@ internal static bool TryGetSyntax(SyntaxNode root, int position, ISyntaxFactsSer
}
/// Error tolerance case for
/// "foo[]"
/// "foo[$$]" or "foo?[$$]"
/// which is parsed as an ArrayTypeSyntax variable declaration instead of an ElementAccessExpression
private static class IncompleteElementAccessExpression
{
......@@ -314,5 +343,43 @@ internal static bool TryGetSyntax(SyntaxNode root, int position, ISyntaxFactsSer
return false;
}
}
/// Error tolerance case for
/// "new String()?[$$]"
/// which is parsed as a BracketedArgumentListSyntax parented by an ElementBindingExpressionSyntax parented by a ConditionalAccessExpressionSyntax
private static class ConditionalAccessExpression
{
internal static bool IsTriggerToken(SyntaxToken token)
{
return !token.IsKind(SyntaxKind.None) &&
token.ValueText.Length == 1 &&
IsTriggerCharacterInternal(token.ValueText[0]) &&
token.Parent is BracketedArgumentListSyntax &&
token.Parent.Parent is ElementBindingExpressionSyntax &&
token.Parent.Parent.Parent is ConditionalAccessExpressionSyntax;
}
internal static bool IsArgumentListToken(ElementBindingExpressionSyntax expression, SyntaxToken token)
{
return expression.ArgumentList.Span.Contains(token.SpanStart) &&
token != expression.ArgumentList.CloseBracketToken;
}
internal static bool TryGetSyntax(SyntaxNode root, int position, ISyntaxFactsService syntaxFacts, SignatureHelpTriggerReason triggerReason, CancellationToken cancellationToken, out ExpressionSyntax identifier, out SyntaxToken openBrace)
{
ElementBindingExpressionSyntax elementBindingExpression;
if (CommonSignatureHelpUtilities.TryGetSyntax(root, position, syntaxFacts, triggerReason, IsTriggerToken, IsArgumentListToken, cancellationToken, out elementBindingExpression))
{
identifier = ((ConditionalAccessExpressionSyntax)elementBindingExpression.Parent).Expression;
openBrace = elementBindingExpression.ArgumentList.OpenBracketToken;
return true;
}
identifier = null;
openBrace = default(SyntaxToken);
return false;
}
}
}
}
......@@ -810,6 +810,20 @@ public void foo()
Test(markup, expectedOrderedItems);
}
[WorkItem(32, "https://github.com/dotnet/roslyn/issues/32")]
[Fact, Trait(Traits.Feature, Traits.Features.SignatureHelp)]
public void NonIdentifierConditionalIndexer()
{
var expected = new[] { new SignatureHelpTestItem("char string[int index]") };
Test(@"class C { void M() { """"?[$$ } }", expected); // inline with a string literal
Test(@"class C { void M() { """"?[/**/$$ } }", expected); // inline with a string literal and multiline comment
Test(@"class C { void M() { ("""")?[$$ } }", expected); // parenthesized expression
Test(@"class C { void M() { new System.String(' ', 1)?[$$ } }", expected); // new object expression
// more complicated parenthesized expression
Test(@"class C { void M() { (null as System.Collections.Generic.List<int>)?[$$ } }", new[] { new SignatureHelpTestItem("int System.Collections.Generic.List<int>[int index]") });
}
[WorkItem(1067933)]
[Fact, Trait(Traits.Feature, Traits.Features.SignatureHelp)]
public void InvokedWithNoToken()
......
......@@ -932,6 +932,47 @@ End Class
Test(markup, expected, experimental:=True)
End Sub
<Fact, Trait(Traits.Feature, Traits.Features.SignatureHelp)>
Public Sub NonIdentifierConditionalIndexer()
Dim expected = {New SignatureHelpTestItem("String(index As Integer) As Char")}
' inline with a string literal
Test("
Class C
Sub M()
Dim c = """"?($$
End Sub
End Class
", expected)
' parenthesized expression
Test("
Class C
Sub M()
Dim c = ("""")?($$
End Sub
End Class
", expected)
' new object expression
Test("
Class C
Sub M()
Dim c = (New System.String("" ""c, 1))?($$
End Sub
End Class
", expected)
' more complicated parenthesized expression
Test("
Class C
Sub M()
Dim c = (CType(Nothing, System.Collections.Generic.List(Of Integer)))?($$
End Sub
End Class
", {New SignatureHelpTestItem("System.Collections.Generic.List(Of Integer)(index As Integer) As Integer")})
End Sub
<Fact, Trait(Traits.Feature, Traits.Features.SignatureHelp)>
Public Sub TestTriggerCharacters()
Dim expectedTriggerCharacters() As Char = {","c, "("c}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册