Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,260 @@ public MyClass1()
$$
}
}
}";
await VerifyCustomCommitProviderAsync(markupBeforeCommit, ItemToCommit, expectedCodeAfterCommit);
}

[WpfTheory]
[InlineData("class")]
[InlineData("enum")]
[InlineData("interface")]
[InlineData("record")]
[InlineData("struct")]
[InlineData("record class")]
[InlineData("record struct")]
[Trait(Traits.Feature, Traits.Features.Completion)]
public async Task InsertConstructorSnippetHasNestedTypeTest(string keyword)
{
var markupBeforeCommit =
$$"""
class Outer
{
$$
{{keyword}} Inner
{
}
}
""";

var expectedCodeAfterCommit =
$$"""
class Outer
{
public Outer()
{
$$
}
{{keyword}} Inner
{
}
}
""";

await VerifyCustomCommitProviderAsync(markupBeforeCommit, ItemToCommit, expectedCodeAfterCommit);
}

[WpfFact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task InsertConstructorSnippetHasNestedClassGluedOuterTest()
{
var markupBeforeCommit_GluedToOuter =
@"class Outer
{$$
class Inner
{
}
}";
// The position ($$) is in a strange position because code does not format after insertion.
var expectedCodeAfterCommit_GluedToOuter =
@"class Outer
{ public Outer()
{

$$ }
class Inner
{
}
}";
await VerifyCustomCommitProviderAsync(markupBeforeCommit_GluedToOuter, ItemToCommit, expectedCodeAfterCommit_GluedToOuter);
}

[WpfTheory]
[InlineData("class")]
[InlineData("record")]
[InlineData("struct")]
[InlineData("record class")]
[InlineData("record struct")]
[Trait(Traits.Feature, Traits.Features.Completion)]
public async Task InsertConstructorSnippetInConstructableTypeTest(string keyword)
{
var markupBeforeCommit =
$$"""
{{keyword}} MyName
{
$$
}
""";

var expectedCodeAfterCommit =
$$"""
{{keyword}} MyName
{
public MyName()
{
$$
}
}
""";

await VerifyCustomCommitProviderAsync(markupBeforeCommit, ItemToCommit, expectedCodeAfterCommit);
}

[WpfFact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task InsertConstructorSnippetInStaticClassTest()
{
var markupBeforeCommit =
$$"""
static class MyClass
{
$$
}
""";
var expectedCodeAfterCommit =
$$"""
static class MyClass
{
public MyClass()
{
$$
}
}
""";
// Current CSharpConstructorSnippetProvider implementation inserts a constructor even for static classes.
await VerifyCustomCommitProviderAsync(markupBeforeCommit, ItemToCommit, expectedCodeAfterCommit);
}

[WpfTheory]
[InlineData("enum")]
[InlineData("interface")]
[Trait(Traits.Feature, Traits.Features.Completion)]
public async Task InsertConstructorSnippetInNotConstructableTypeTest(string keyword)
{
var markupBeforeCommit =
$$"""
{{keyword}} MyName
{
$$
}
""";
await VerifyItemIsAbsentAsync(markupBeforeCommit, ItemToCommit);
}

[WpfTheory]
[InlineData("class")]
[InlineData("record")]
[InlineData("struct")]
[InlineData("record class")]
[InlineData("record struct")]
[Trait(Traits.Feature, Traits.Features.Completion)]
public async Task InsertConstructorSnippetBraceNotClosed(string keyword)
{
var markupBeforeCommit =
$$"""
namespace N
{
{{keyword}} Outer
{
$$
int i;
""";

var expectedCodeAfterCommit =
$$"""
namespace N
{
{{keyword}} Outer
{
public Outer()
{
$$
}
int i;
""";
await VerifyCustomCommitProviderAsync(markupBeforeCommit, ItemToCommit, expectedCodeAfterCommit);
}

[WpfTheory]
[InlineData("class")]
[InlineData("record")]
[InlineData("struct")]
[InlineData("record class")]
[InlineData("record struct")]
[Trait(Traits.Feature, Traits.Features.Completion)]
public async Task InsertConstructorSnippetBraceNotClosedAndEndOfFile(string keyword)
{
var markupBeforeCommit =
$$"""
namespace N
{
{{keyword}} Outer
{
$$
""";

var expectedCodeAfterCommit =
$$"""
namespace N
{
{{keyword}} Outer
{
public Outer()
{
$$
}
""";
await VerifyCustomCommitProviderAsync(markupBeforeCommit, ItemToCommit, expectedCodeAfterCommit);
}

[WpfTheory]
[InlineData("class")]
[InlineData("record")]
[InlineData("struct")]
[InlineData("record class")]
[InlineData("record struct")]
[Trait(Traits.Feature, Traits.Features.Completion)]
public async Task InsertConstructorSnippetBraceNotClosedAndEndOfFileAndNested(string keyword)
{
var markupBeforeCommit =
$$"""
namespace N
{
{{keyword}} Outer
{
class Inner{}
$$
""";

var expectedCodeAfterCommit =
$$"""
namespace N
{
{{keyword}} Outer
{
class Inner{}
public Outer()
{
$$
}
""";
await VerifyCustomCommitProviderAsync(markupBeforeCommit, ItemToCommit, expectedCodeAfterCommit);
}

[WpfFact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task InsertConstructorSnippetInGenericClassTest()
{
var markupBeforeCommit =
@"class MyClass<T> : BaseClass
{
$$
}";

var expectedCodeAfterCommit =
@"class MyClass<T> : BaseClass
{
public MyClass()
{
$$
}
}";
await VerifyCustomCommitProviderAsync(markupBeforeCommit, ItemToCommit, expectedCodeAfterCommit);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,16 @@ protected override async Task<TextChange> GenerateSnippetTextChangeAsync(Documen
var generator = SyntaxGenerator.GetGenerator(document);
var syntaxFacts = document.GetRequiredLanguageService<ISyntaxFactsService>();
var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var nodeAtPosition = root.FindNode(TextSpan.FromBounds(position, position));
var tokenAtPosition = root.FindTokenOnLeftOfPosition(position);
var nodeAtPosition = root.FindNode(tokenAtPosition.Span);

// Skip inner class in nested class if position is out of it.
// For example "class Outer{ class Inner {} ctor@ }"
if (tokenAtPosition.RawKind == syntaxFacts.SyntaxKinds.CloseBraceToken)
{
Contract.ThrowIfNull(nodeAtPosition.Parent);
nodeAtPosition = nodeAtPosition.Parent;
}
var containingType = nodeAtPosition.FirstAncestorOrSelf<SyntaxNode>(syntaxFacts.IsTypeDeclaration);
Contract.ThrowIfNull(containingType);
var constructorDeclaration = generator.ConstructorDeclaration(
Expand Down