diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/Snippets/CSharpInterfaceSnippetCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/Snippets/CSharpInterfaceSnippetCompletionProviderTests.cs new file mode 100644 index 0000000000000..9f85df7d0f60b --- /dev/null +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/Snippets/CSharpInterfaceSnippetCompletionProviderTests.cs @@ -0,0 +1,239 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Completion.CompletionProviders.Snippets +{ + [Trait(Traits.Feature, Traits.Features.Completion)] + public class CSharpInterfaceSnippetCompletionProviderTests : AbstractCSharpSnippetCompletionProviderTests + { + protected override string ItemToCommit => "interface"; + + [WpfFact] + public async Task InsertInterfaceSnippetInNamespaceTest() + { + var markupBeforeCommit = +@"namespace Namespace +{ + $$ +}"; + + var expectedCodeAfterCommit = +@"namespace Namespace +{ + interface MyInterface + { + $$ + } +}"; + await VerifyCustomCommitProviderAsync(markupBeforeCommit, ItemToCommit, expectedCodeAfterCommit); + } + + [WpfFact] + public async Task InsertInterfaceSnippetInFileScopedNamespaceTest() + { + var markupBeforeCommit = +@"namespace Namespace; + +$$"; + + var expectedCodeAfterCommit = +@"namespace Namespace; + +interface MyInterface +{ + $$ +}"; + await VerifyCustomCommitProviderAsync(markupBeforeCommit, ItemToCommit, expectedCodeAfterCommit); + } + + [WpfFact] + public async Task InsertInterfaceSnippetTest() + { + var markupBeforeCommit = +@"$$"; + + var expectedCodeAfterCommit = +@"interface MyInterface +{ + $$ +}"; + await VerifyCustomCommitProviderAsync(markupBeforeCommit, ItemToCommit, expectedCodeAfterCommit); + } + + [WpfFact] + public async Task InsertInterfaceTopLevelSnippetTest() + { + var markupBeforeCommit = +@"System.Console.WriteLine(); +$$"; + + var expectedCodeAfterCommit = +@"System.Console.WriteLine(); + +interface MyInterface +{ + $$ +}"; + await VerifyCustomCommitProviderAsync(markupBeforeCommit, ItemToCommit, expectedCodeAfterCommit); + } + + [WpfFact] + public async Task InsertInterfaceSnippetInClassTest() + { + var markupBeforeCommit = +@"class MyClass +{ + $$ +}"; + + var expectedCodeAfterCommit = +@"class MyClass +{ + interface MyInterface + { + $$ + } +}"; + await VerifyCustomCommitProviderAsync(markupBeforeCommit, ItemToCommit, expectedCodeAfterCommit); + } + + [WpfFact] + public async Task InsertInterfaceSnippetInRecordTest() + { + var markupBeforeCommit = +@"record MyRecord +{ + $$ +}"; + + var expectedCodeAfterCommit = +@"record MyRecord +{ + interface MyInterface + { + $$ + } +}"; + await VerifyCustomCommitProviderAsync(markupBeforeCommit, ItemToCommit, expectedCodeAfterCommit); + } + + [WpfFact] + public async Task InsertInterfaceSnippetInStructTest() + { + var markupBeforeCommit = +@"struct MyStruct +{ + $$ +}"; + + var expectedCodeAfterCommit = +@"struct MyStruct +{ + interface MyInterface + { + $$ + } +}"; + await VerifyCustomCommitProviderAsync(markupBeforeCommit, ItemToCommit, expectedCodeAfterCommit); + } + + [WpfFact] + public async Task InsertInterfaceSnippetInInterfaceTest() + { + var markupBeforeCommit = +@"interface MyInterface +{ + $$ +}"; + + var expectedCodeAfterCommit = +@"interface MyInterface +{ + interface MyInterface1 + { + $$ + } +}"; + await VerifyCustomCommitProviderAsync(markupBeforeCommit, ItemToCommit, expectedCodeAfterCommit); + } + + [WpfFact] + public async Task InsertInterfaceSnippetWithModifiersTest() + { + var markupBeforeCommit = + $@" + + + +$$ + + +root = true + +[*] +# IDE0008: Use explicit type +dotnet_style_require_accessibility_modifiers = always + + +"; + var expectedCodeAfterCommit = + $@" +public interface MyInterface +{{ + $$ +}} +"; + await VerifyCustomCommitProviderAsync(markupBeforeCommit, ItemToCommit, expectedCodeAfterCommit); + } + + [WpfFact] + public async Task NoInterfaceSnippetInEnumTest() + { + var markupBeforeCommit = +@"enum MyEnum +{ + $$ +}"; + + await VerifyItemIsAbsentAsync(markupBeforeCommit, ItemToCommit); + } + + [WpfFact] + public async Task NoInteraceSnippetInMethodTest() + { + var markupBeforeCommit = +@"class Program +{ + public void Method() + { + $$ + } +}"; + await VerifyItemIsAbsentAsync(markupBeforeCommit, ItemToCommit); + } + + [WpfFact] + public async Task NoInterfaceSnippetInConstructorTest() + { + var markupBeforeCommit = +@"class Program +{ + public Program() + { + $$ + } +}"; + await VerifyItemIsAbsentAsync(markupBeforeCommit, ItemToCommit); + } + } +} diff --git a/src/Features/CSharp/Portable/Snippets/CSharpInterfaceSnippetProvider.cs b/src/Features/CSharp/Portable/Snippets/CSharpInterfaceSnippetProvider.cs new file mode 100644 index 0000000000000..87b37095e1be1 --- /dev/null +++ b/src/Features/CSharp/Portable/Snippets/CSharpInterfaceSnippetProvider.cs @@ -0,0 +1,50 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Composition; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Utilities; +using Microsoft.CodeAnalysis.Snippets; +using Microsoft.CodeAnalysis.Snippets.SnippetProviders; + +namespace Microsoft.CodeAnalysis.CSharp.Snippets +{ + [ExportSnippetProvider(nameof(ISnippetProvider), LanguageNames.CSharp), Shared] + internal class CSharpInterfaceSnippetProvider : CSharpTypeSnippetProvider + { + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public CSharpInterfaceSnippetProvider() + { + } + + public override string SnippetIdentifier => "interface"; + + public override string SnippetDescription => FeaturesResources.interface_; + + protected override async Task GenerateTypeDeclarationAsync(Document document, int position, bool useAccessibility, CancellationToken cancellationToken) + { + var generator = SyntaxGenerator.GetGenerator(document); + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + + var name = NameGenerator.GenerateUniqueName("MyInterface", name => semanticModel.LookupSymbols(position, name: name).IsEmpty); + return useAccessibility is true + ? generator.InterfaceDeclaration(name, accessibility: Accessibility.Public) + : generator.InterfaceDeclaration(name); + } + + protected override Func GetSnippetContainerFunction(ISyntaxFacts syntaxFacts) + { + return syntaxFacts.IsInterfaceDeclaration; + } + } +} diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs index cb8392ed54f88..8a1c40805b0f0 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs @@ -124,6 +124,7 @@ public TSyntaxKind Convert(int kind) where TSyntaxKind : struct public int Attribute => (int)SyntaxKind.Attribute; public int ClassDeclaration => (int)SyntaxKind.ClassDeclaration; + public int InterfaceDeclaration => (int)SyntaxKind.InterfaceDeclaration; public int? RecordDeclaration => (int)SyntaxKind.RecordDeclaration; public int? RecordStructDeclaration => (int)SyntaxKind.RecordStructDeclaration; public int? StructDeclaration => (int)SyntaxKind.StructDeclaration; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs index 46ed2837ff877..6b9228b623e5e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs @@ -923,6 +923,9 @@ public static bool IsClassDeclaration(this ISyntaxFacts syntaxFacts, [NotNullWhe public static bool IsGlobalAttribute(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) => syntaxFacts.IsGlobalAssemblyAttribute(node) || syntaxFacts.IsGlobalModuleAttribute(node); + public static bool IsInterfaceDeclaration(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) + => node?.RawKind == syntaxFacts.SyntaxKinds.InterfaceDeclaration; + public static bool IsParameter(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) => node?.RawKind == syntaxFacts.SyntaxKinds.Parameter; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs index 8e6c6211c16e8..b6a15d53c6cb0 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs @@ -175,6 +175,7 @@ internal interface ISyntaxKinds int Attribute { get; } int ClassDeclaration { get; } + int InterfaceDeclaration { get; } int? RecordDeclaration { get; } int? RecordStructDeclaration { get; } int? StructDeclaration { get; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb index f418b5fd9e5b9..1ce6a58163ecf 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb @@ -128,6 +128,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService Public ReadOnly Property Attribute As Integer = SyntaxKind.Attribute Implements ISyntaxKinds.Attribute Public ReadOnly Property ClassDeclaration As Integer = SyntaxKind.ClassBlock Implements ISyntaxKinds.ClassDeclaration + Public ReadOnly Property InterfaceDeclaration As Integer = SyntaxKind.InterfaceBlock Implements ISyntaxKinds.InterfaceDeclaration Public ReadOnly Property RecordDeclaration As Integer? Implements ISyntaxKinds.RecordDeclaration Public ReadOnly Property RecordStructDeclaration As Integer? Implements ISyntaxKinds.RecordStructDeclaration Public ReadOnly Property StructDeclaration As Integer? Implements ISyntaxKinds.StructDeclaration