Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support abstract methods and records in complete statement command #48128

Merged
merged 12 commits into from
Apr 8, 2021
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ private static void MoveCaretToSemicolonPosition(
return;
}
else if (syntaxFacts.IsStatement(currentNode)
|| currentNode.IsKind(SyntaxKind.FieldDeclaration, SyntaxKind.DelegateDeclaration, SyntaxKind.ArrowExpressionClause))
|| CanHaveSemicolon(currentNode))
{
MoveCaretToFinalPositionInStatement(currentNode, args, caret, isInsideDelimiters);
return;
Expand All @@ -203,6 +203,30 @@ private static void MoveCaretToSemicolonPosition(
}
}

private static bool CanHaveSemicolon(SyntaxNode currentNode)
{
if (currentNode.IsKind(SyntaxKind.FieldDeclaration, SyntaxKind.DelegateDeclaration, SyntaxKind.ArrowExpressionClause))
{
return true;
}

if (currentNode is RecordDeclarationSyntax { OpenBraceToken: { IsMissing: true } })
{
return true;
}

if (currentNode is MethodDeclarationSyntax method)
{
if (method.Modifiers.Any(SyntaxKind.AbstractKeyword) || method.Modifiers.Any(SyntaxKind.ExternKeyword) ||
method.IsParentKind(SyntaxKind.InterfaceDeclaration))
Comment on lines +220 to +221
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❔ Is there any harm in making this unconditional?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Note that partial methods also can be declared without a body.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sharwell I think it will unnecessarily move the caret to the end for typos, which can be annoying.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Note that partial methods also can be declared without a body.

That's a good case. Going to fix it and add a test.

{
return true;
}
}

return false;
}

private static bool IsInConditionOfDoStatement(SyntaxNode currentNode, SnapshotPoint caret)
{
if (!currentNode.IsKind(SyntaxKind.DoStatement, out DoStatementSyntax doStatement))
Expand Down Expand Up @@ -257,6 +281,8 @@ private static bool TryGetCaretPositionToMove(SyntaxNode statementNode, Snapshot
case SyntaxKind.FieldDeclaration:
case SyntaxKind.DelegateDeclaration:
case SyntaxKind.ArrowExpressionClause:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.RecordDeclaration:
// These statement types end in a semicolon.
// if the original caret was inside any delimiters, `caret` will be after the outermost delimiter
targetPosition = caret;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,15 @@ internal static int MethodM(int a, int b)
#region ParameterList

[WpfTheory, Trait(Traits.Feature, Traits.Features.CompleteStatement)]
[InlineData("extern void M(object o$$)", "extern void M(object o)")]
[InlineData("abstract void M(object o$$)", "abstract void M(object o)")]
[InlineData("abstract void M($$object o)", "abstract void M(object o)")]
[InlineData("abstract void M(object o = default(object$$))", "abstract void M(object o = default(object))")]
[InlineData("abstract void M(object o = default($$object))", "abstract void M(object o = default(object))")]
[InlineData("abstract void M(object o = $$default(object))", "abstract void M(object o = default(object))")]
[InlineData("public record C(int X, $$int Y)", "public record C(int X, int Y)")]
[InlineData("public record C(int X, int$$ Y)", "public record C(int X, int Y)")]
[InlineData("public record C(int X, int Y$$)", "public record C(int X, int Y)")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i see tests added for records, but nothing new for the 'abstract method' code you added. do we need tests for that?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CyrusNajmabadi Abstract methods already have tests. But the test was disabled. I added record tests (in addition to the existing for abstract methods), and enabled the test.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok!

public void ParameterList_CouldBeHandled(string signature, string expectedSignature)
Youssef1313 marked this conversation as resolved.
Show resolved Hide resolved
{
var code = $@"
Expand All @@ -61,9 +65,25 @@ public class Class1
{expectedSignature};$$
}}";

// These cases are not currently handled. If support is added in the future, 'expected' should be correct.
_ = expected;
VerifyNoSpecialSemicolonHandling(code);
VerifyTypingSemicolon(code, expected);
}

[WpfFact, Trait(Traits.Feature, Traits.Features.CompleteStatement)]
public void ParameterList_InterfaceMethod()
{
var code = @"
public interface I
{
public void M(object o$$)
}";

var expected = @"
public interface I
{
public void M(object o);$$
}";

VerifyTypingSemicolon(code, expected);
}

[WpfTheory, Trait(Traits.Feature, Traits.Features.CompleteStatement)]
Expand Down