提交 821ae7dc 编写于 作者: Š Šimon Koníček

Adding 'when' keyword completion for any switch case, not just after a declaration pattern

上级 87753710
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 // 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.Threading.Tasks;
using Roslyn.Test.Utilities;
......@@ -9,56 +9,56 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations
public class WhenKeywordRecommenderTests : KeywordRecommenderTests
{
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterCatch()
public async Task TestForCatchClause_AfterCatch()
{
await VerifyKeywordAsync(AddInsideMethod(
@"try {} catch $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterCatchDeclaration1()
public async Task TestForCatchClause_AfterCatchDeclaration1()
{
await VerifyKeywordAsync(AddInsideMethod(
@"try {} catch (Exception) $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterCatchDeclaration2()
public async Task TestForCatchClause_AfterCatchDeclaration2()
{
await VerifyKeywordAsync(AddInsideMethod(
@"try {} catch (Exception e) $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterCatchDeclarationEmpty()
public async Task TestForCatchClause_AfterCatchDeclarationEmpty()
{
await VerifyKeywordAsync(AddInsideMethod(
@"try {} catch () $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestNotAfterTryBlock()
public async Task TestForCatchClause_NotAfterTryBlock()
{
await VerifyAbsenceAsync(AddInsideMethod(
@"try {} $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestNotAfterFilter1()
public async Task TestForCatchClause_NotAfterFilter1()
{
await VerifyAbsenceAsync(AddInsideMethod(
@"try {} catch (Exception e) when $$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestNotAfterFilter2()
public async Task TestForCatchClause_NotAfterFilter2()
{
await VerifyAbsenceAsync(AddInsideMethod(
@"try {} catch (Exception e) when ($$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestNotAfterFilter3()
public async Task TestForCatchClause_NotAfterFilter3()
{
await VerifyAbsenceAsync(AddInsideMethod(
@"try {} catch (Exception e) when (true) $$"));
......@@ -66,10 +66,97 @@ public async Task TestNotAfterFilter3()
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
[WorkItem(24113, "https://github.com/dotnet/roslyn/issues/24113")]
public async Task TestInCasePattern()
public async Task TestForSwitchCase_AfterDeclarationPattern()
{
await VerifyKeywordAsync(AddInsideMethod(
@"switch (1) { case int i $$ }"));
await VerifyKeywordAsync(AddInsideMethod(@"switch (1) { case int i $$ }"));
await VerifyKeywordAsync(AddInsideMethod(@"switch (1) { case int i $$: }"));
await VerifyKeywordAsync(AddInsideMethod(@"switch (1) { case int i $$ break; }"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestForSwitchCase_AfterLiteral()
{
await VerifyKeywordAsync(AddInsideMethod(@"switch (1) { case 1 $$ }"));
await VerifyKeywordAsync(AddInsideMethod(@"switch (1) { case 1 $$: }"));
await VerifyKeywordAsync(AddInsideMethod(@"switch (1) { case 1 $$ break; }"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestForSwitchCase_AfterBinaryExpression()
{
await VerifyKeywordAsync(AddInsideMethod(@"switch (1) { case 1 + 1 $$ }"));
await VerifyKeywordAsync(AddInsideMethod(@"switch (1) { case 1 + 1 $$: }"));
await VerifyKeywordAsync(AddInsideMethod(@"switch (1) { case 1 + 1 $$ break; }"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestForSwitchCase_AfterParenthesesWithMissingTokenInside()
{
await VerifyKeywordAsync(AddInsideMethod(@"switch (1) { case (1 + ) $$ }"));
await VerifyKeywordAsync(AddInsideMethod(@"switch (1) { case (1 + ) $$: }"));
await VerifyKeywordAsync(AddInsideMethod(@"switch (1) { case (1 + ) $$ break; }"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestForSwitchCase_NotAfterMissingTokenInBinaryExpression()
{
await VerifyAbsenceAsync(AddInsideMethod(@"switch (1) { case 1 + $$ }"));
await VerifyAbsenceAsync(AddInsideMethod(@"switch (1) { case 1 + $$: }"));
await VerifyAbsenceAsync(AddInsideMethod(@"switch (1) { case 1 + $$ break; }"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestForSwitchCase_NotAfterMissingCloseParen()
{
await VerifyAbsenceAsync(AddInsideMethod(@"switch (1) { case (1 + 1 $$ }"));
await VerifyAbsenceAsync(AddInsideMethod(@"switch (1) { case (1 + 1 $$: }"));
await VerifyAbsenceAsync(AddInsideMethod(@"switch (1) { case (1 + 1 $$ break; }"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestForSwitchCase_NotInsideParentheses()
{
await VerifyAbsenceAsync(AddInsideMethod(@"switch (1) { case (1 + 1 $$) }"));
await VerifyAbsenceAsync(AddInsideMethod(@"switch (1) { case (1 + 1 $$): }"));
await VerifyAbsenceAsync(AddInsideMethod(@"switch (1) { case (1 + 1 $$) break; }"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestForSwitchCase_NotAfterNew()
{
await VerifyAbsenceAsync(AddInsideMethod(@"switch (1) { case new $$ }"));
await VerifyAbsenceAsync(AddInsideMethod(@"switch (1) { case new $$: }"));
await VerifyAbsenceAsync(AddInsideMethod(@"switch (1) { case new $$ break; }"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestForSwitchCase_NotAfterCase()
{
await VerifyAbsenceAsync(AddInsideMethod(@"switch (1) { case $$ }"));
await VerifyAbsenceAsync(AddInsideMethod(@"switch (1) { case $$: }"));
await VerifyAbsenceAsync(AddInsideMethod(@"switch (1) { case $$ break; }"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestForSwitchCase_NotAfterDefault()
{
await VerifyAbsenceAsync(AddInsideMethod(@"switch (1) { default $$ }"));
await VerifyAbsenceAsync(AddInsideMethod(@"switch (1) { default $$: }"));
await VerifyAbsenceAsync(AddInsideMethod(@"switch (1) { default $$ break; }"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestForSwitchCase_NotAfterWhen()
{
await VerifyAbsenceAsync(AddInsideMethod(@"switch (1) { case 1 when $$ }"));
await VerifyAbsenceAsync(AddInsideMethod(@"switch (1) { case 1 when $$: }"));
await VerifyAbsenceAsync(AddInsideMethod(@"switch (1) { case 1 when $$ break; }"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestForSwitchCase_NotInEmptySwitchStatement()
{
await VerifyAbsenceAsync(AddInsideMethod(@"switch (1) { $$ }"));
}
}
}
// 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.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders
{
......@@ -15,26 +17,57 @@ public WhenKeywordRecommender()
protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
{
if (context.IsCatchFilterContext)
return context.IsCatchFilterContext || IsAfterCompleteExpressionOrPatternInCaseLabel(context);
}
private static bool IsAfterCompleteExpressionOrPatternInCaseLabel(CSharpSyntaxContext context)
{
var switchLabel = GetAncestorUntilStatement<SwitchLabelSyntax>(context.TargetToken);
if (switchLabel == null)
{
return true;
return false;
}
// case int i $$
// case int i w$$
if (IsIdentifierInCasePattern(context.TargetToken.Parent))
var expressionOrPattern = switchLabel.ChildNodes().FirstOrDefault();
if (expressionOrPattern == null)
{
return true;
return false;
}
return false;
// If the last token missing, the expression is incomplete - possibly because of missing parentheses,
// but not necessarily. We don't want to offer 'when' in those cases. Here are some examples that illustrate this:
// case |
// case 1 + |
// case (1 + 1 |
// case new |
// Also note that if there's a missing token inside the expression, that's fine and we do offer 'when':
// case (1 + ) |
var lastToken = expressionOrPattern.GetLastToken(includeZeroWidth: true);
// Only offer if we're at the last token of the expression/pattern, not inside it.
// Notice that when lastToken is missing, it will never compare equal to context.TargetToken
// since TargetToken skips missing tokens. So if this passes, we're all good!
return context.TargetToken == lastToken;
}
private static bool IsIdentifierInCasePattern(SyntaxNode node)
private static T GetAncestorUntilStatement<T>(SyntaxToken token) where T : SyntaxNode
{
return node.IsKind(SyntaxKind.SingleVariableDesignation)
&& node.IsParentKind(SyntaxKind.DeclarationPattern)
&& node.Parent.IsParentKind(SyntaxKind.CasePatternSwitchLabel);
for (var node = token.Parent; node != null; node = node.Parent)
{
if (node is StatementSyntax)
{
break;
}
if (node is T tNode)
{
return tNode;
}
}
return null;
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册