diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/Snippets/AbstractCSharpAutoPropertyCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/Snippets/AbstractCSharpAutoPropertyCompletionProviderTests.cs new file mode 100644 index 0000000000000..d1be6609f3414 --- /dev/null +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/Snippets/AbstractCSharpAutoPropertyCompletionProviderTests.cs @@ -0,0 +1,148 @@ +// 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.Threading.Tasks; +using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.Testing; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Completion.CompletionProviders.Snippets +{ + public abstract class AbstractCSharpAutoPropertyCompletionProviderTests : AbstractCSharpSnippetCompletionProviderTests + { + protected abstract string GetDefaultPropertyText(string propertyName); + + [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task MissingInNamespace() + { + await VerifyPropertyAbsenceAsync(""" + namespace Namespace + { + $$ + } + """); + } + + [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task MissingInFilescopedNamespace() + { + await VerifyPropertyAbsenceAsync(""" + namespace Namespace; + + $$ + """); + } + + [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task MissingInTopLevelContext() + { + await VerifyPropertyAbsenceAsync(""" + System.Console.WriteLine(); + $$ + """); + } + + [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task InsertSnippetInClass() + { + await VerifyDefaultPropertyAsync(""" + class MyClass + { + $$ + } + """); + } + + [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task InsertSnippetInRecord() + { + await VerifyDefaultPropertyAsync(""" + record MyRecord + { + $$ + } + """); + } + + [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task InsertSnippetInStruct() + { + await VerifyDefaultPropertyAsync(""" + struct MyStruct + { + $$ + } + """); + } + + // This case might produce non-default results for different snippets (e.g. no `set` accessor in 'propg' snippet), + // so it is tested separately for all of them + [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] + public abstract Task InsertSnippetInInterface(); + + [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task InsertSnippetNaming() + { + await VerifyDefaultPropertyAsync(""" + class MyClass + { + public int MyProperty { get; set; } + $$ + } + """, "MyProperty1"); + } + + [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task MissingInEnum() + { + await VerifyPropertyAbsenceAsync(""" + enum MyEnum + { + $$ + } + """); + } + + [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task MissingInMethod() + { + await VerifyPropertyAbsenceAsync(""" + class Program + { + public void Method() + { + $$ + } + } + """); + } + + [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task MissingInConstructor() + { + await VerifyPropertyAbsenceAsync(""" + class Program + { + public Program() + { + $$ + } + } + """); + } + + private Task VerifyPropertyAbsenceAsync(string markup) => VerifyItemIsAbsentAsync(markup, ItemToCommit); + + protected async Task VerifyPropertyAsync(string markup, string propertyText) + { + TestFileMarkupParser.GetPosition(markup, out var code, out var position); + var expectedCode = code.Insert(position, propertyText + "$$"); + await VerifyCustomCommitProviderAsync(markup, ItemToCommit, expectedCode); + } + + protected Task VerifyDefaultPropertyAsync(string markup, string propertyName = "MyProperty") + => VerifyPropertyAsync(markup, GetDefaultPropertyText(propertyName)); + } +} diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/Snippets/CSharpPropSnippetCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/Snippets/CSharpPropSnippetCompletionProviderTests.cs index 63110bcf0a40a..f2898d3713c9e 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/Snippets/CSharpPropSnippetCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/Snippets/CSharpPropSnippetCompletionProviderTests.cs @@ -2,179 +2,25 @@ // 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 { - public class CSharpPropSnippetCompletionProviderTests : AbstractCSharpSnippetCompletionProviderTests + public class CSharpPropSnippetCompletionProviderTests : AbstractCSharpAutoPropertyCompletionProviderTests { protected override string ItemToCommit => "prop"; - [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] - public async Task PropSnippetMissingInNamespace() - { - var markupBeforeCommit = -@"namespace Namespace -{ - $$ -}"; - - await VerifyItemIsAbsentAsync(markupBeforeCommit, ItemToCommit); - } + protected override string GetDefaultPropertyText(string propertyName) + => $"public int {propertyName} {{ get; set; }}"; - [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] - public async Task PropSnippetMissingInFilescopedNamespace() + public override async Task InsertSnippetInInterface() { - var markupBeforeCommit = -@"namespace Namespace; - -$$"; - - await VerifyItemIsAbsentAsync(markupBeforeCommit, ItemToCommit); - } - - [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] - public async Task PropSnippetMissingInTopLevelContext() - { - var markupBeforeCommit = -@"System.Console.WriteLine(); -$$"; - - await VerifyItemIsAbsentAsync(markupBeforeCommit, ItemToCommit); - } - - [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] - public async Task InsertPropSnippetInClassTest() - { - var markupBeforeCommit = -@"class MyClass -{ - $$ -}"; - - var expectedCodeAfterCommit = -@"class MyClass -{ - public int MyProperty { get; set; }$$ -}"; - await VerifyCustomCommitProviderAsync(markupBeforeCommit, ItemToCommit, expectedCodeAfterCommit); - } - - [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] - public async Task InsertPropSnippetInRecordTest() - { - var markupBeforeCommit = -@"record MyRecord -{ - $$ -}"; - - var expectedCodeAfterCommit = -@"record MyRecord -{ - public int MyProperty { get; set; }$$ -}"; - await VerifyCustomCommitProviderAsync(markupBeforeCommit, ItemToCommit, expectedCodeAfterCommit); - } - - [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] - public async Task InsertPropSnippetInStructTest() - { - var markupBeforeCommit = -@"struct MyStruct -{ - $$ -}"; - - var expectedCodeAfterCommit = -@"struct MyStruct -{ - public int MyProperty { get; set; }$$ -}"; - await VerifyCustomCommitProviderAsync(markupBeforeCommit, ItemToCommit, expectedCodeAfterCommit); - } - - [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] - public async Task InsertPropSnippetInInterfaceTest() - { - var markupBeforeCommit = -@"interface MyInterface -{ - $$ -}"; - - var expectedCodeAfterCommit = -@"interface MyInterface -{ - public int MyProperty { get; set; }$$ -}"; - await VerifyCustomCommitProviderAsync(markupBeforeCommit, ItemToCommit, expectedCodeAfterCommit); - } - - [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] - public async Task InsertPropSnippetNamingTest() - { - var markupBeforeCommit = -@"class MyClass -{ - public int MyProperty { get; set; } - $$ -}"; - - var expectedCodeAfterCommit = -@"class MyClass -{ - public int MyProperty { get; set; } - public int MyProperty1 { get; set; }$$ -}"; - await VerifyCustomCommitProviderAsync(markupBeforeCommit, ItemToCommit, expectedCodeAfterCommit); - } - - [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] - public async Task NoPropSnippetInEnumTest() - { - var markupBeforeCommit = -@"enum MyEnum -{ - $$ -}"; - - await VerifyItemIsAbsentAsync(markupBeforeCommit, ItemToCommit); - } - - [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] - public async Task NoPropSnippetInMethodTest() - { - var markupBeforeCommit = -@"class Program -{ - public void Method() - { - $$ - } -}"; - await VerifyItemIsAbsentAsync(markupBeforeCommit, ItemToCommit); - } - - [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] - public async Task NoPropSnippetInConstructorTest() - { - var markupBeforeCommit = -@"class Program -{ - public Program() - { - $$ - } -}"; - await VerifyItemIsAbsentAsync(markupBeforeCommit, ItemToCommit); + await VerifyDefaultPropertyAsync(""" + interface MyInterface + { + $$ + } + """); } } } diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/Snippets/CSharpPropgSnippetCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/Snippets/CSharpPropgSnippetCompletionProviderTests.cs new file mode 100644 index 0000000000000..945b47bbe0323 --- /dev/null +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/Snippets/CSharpPropgSnippetCompletionProviderTests.cs @@ -0,0 +1,27 @@ +// 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.Threading.Tasks; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Completion.CompletionProviders.Snippets +{ + public class CSharpPropgSnippetCompletionProviderTests : AbstractCSharpAutoPropertyCompletionProviderTests + { + protected override string ItemToCommit => "propg"; + + protected override string GetDefaultPropertyText(string propertyName) + => $"public int {propertyName} {{ get; private set; }}"; + + public override async Task InsertSnippetInInterface() + { + // Ensure we don't generate redundant `set` accessor when executed in interface + await VerifyPropertyAsync(""" + interface MyInterface + { + $$ + } + """, "public int MyProperty { get; }"); + } + } +} diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/Snippets/CSharpPropiSnippetCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/Snippets/CSharpPropiSnippetCompletionProviderTests.cs new file mode 100644 index 0000000000000..685ce642be9cf --- /dev/null +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/Snippets/CSharpPropiSnippetCompletionProviderTests.cs @@ -0,0 +1,26 @@ +// 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.Threading.Tasks; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Completion.CompletionProviders.Snippets +{ + public class CSharpPropiSnippetCompletionProviderTests : AbstractCSharpAutoPropertyCompletionProviderTests + { + protected override string ItemToCommit => "propi"; + + protected override string GetDefaultPropertyText(string propertyName) + => $"public int {propertyName} {{ get; init; }}"; + + public override async Task InsertSnippetInInterface() + { + await VerifyDefaultPropertyAsync(""" + interface MyInterface + { + $$ + } + """); + } + } +} diff --git a/src/Features/CSharp/Portable/CSharpFeaturesResources.resx b/src/Features/CSharp/Portable/CSharpFeaturesResources.resx index 97f4896fe310a..377ba2099a93f 100644 --- a/src/Features/CSharp/Portable/CSharpFeaturesResources.resx +++ b/src/Features/CSharp/Portable/CSharpFeaturesResources.resx @@ -638,4 +638,7 @@ Selection cannot be in constructor initializer + + init-only property + \ No newline at end of file diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/SnippetCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/SnippetCompletionProvider.cs index 78acc22d76182..2ef9aa564b822 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/SnippetCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/SnippetCompletionProvider.cs @@ -33,7 +33,7 @@ internal sealed class SnippetCompletionProvider : LSPCompletionProvider { private static readonly HashSet s_snippetsWithReplacements = new() { - "class", "cw", "ctor", "else", "foreach", "if", "interface", "prop", "struct", "while" + "class", "cw", "ctor", "else", "foreach", "if", "interface", "prop", "propg", "struct", "while" }; internal override bool IsSnippetProvider => true; diff --git a/src/Features/CSharp/Portable/Snippets/AbstractCSharpAutoPropertySnippetProvider.cs b/src/Features/CSharp/Portable/Snippets/AbstractCSharpAutoPropertySnippetProvider.cs new file mode 100644 index 0000000000000..f16024c9a023a --- /dev/null +++ b/src/Features/CSharp/Portable/Snippets/AbstractCSharpAutoPropertySnippetProvider.cs @@ -0,0 +1,83 @@ +// 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.Collections.Immutable; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.Extensions; +using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp.Utilities; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Utilities; +using Microsoft.CodeAnalysis.Snippets; +using Microsoft.CodeAnalysis.Snippets.SnippetProviders; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.Snippets +{ + internal abstract class AbstractCSharpAutoPropertySnippetProvider : AbstractPropertySnippetProvider + { + protected virtual AccessorDeclarationSyntax? GenerateGetAccessorDeclaration(CSharpSyntaxContext syntaxContext, SyntaxGenerator generator) + => (AccessorDeclarationSyntax)generator.GetAccessorDeclaration(); + + protected virtual AccessorDeclarationSyntax? GenerateSetAccessorDeclaration(CSharpSyntaxContext syntaxContext, SyntaxGenerator generator) + => (AccessorDeclarationSyntax)generator.SetAccessorDeclaration(); + + protected override async Task IsValidSnippetLocationAsync(Document document, int position, CancellationToken cancellationToken) + { + var syntaxTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); + Contract.ThrowIfNull(syntaxTree); + + return syntaxTree.IsMemberDeclarationContext(position, contextOpt: null, + SyntaxKindSet.AllMemberModifiers, SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, canBePartial: true, cancellationToken); + } + + protected override async Task GenerateSnippetSyntaxAsync(Document document, int position, CancellationToken cancellationToken) + { + var compilation = await document.Project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var generator = SyntaxGenerator.GetGenerator(document); + var identifierName = NameGenerator.GenerateUniqueName("MyProperty", + n => semanticModel.LookupSymbols(position, name: n).IsEmpty); + var syntaxContext = CSharpSyntaxContext.CreateContext(document, semanticModel, position, cancellationToken); + var accessors = new AccessorDeclarationSyntax?[] + { + GenerateGetAccessorDeclaration(syntaxContext, generator), + GenerateSetAccessorDeclaration(syntaxContext, generator), + }; + + return SyntaxFactory.PropertyDeclaration( + attributeLists: default, + modifiers: SyntaxTokenList.Create(SyntaxFactory.Token(SyntaxKind.PublicKeyword)), + type: compilation.GetSpecialType(SpecialType.System_Int32).GenerateTypeSyntax(allowVar: false), + explicitInterfaceSpecifier: null, + identifier: identifierName.ToIdentifierToken(), + accessorList: SyntaxFactory.AccessorList(new SyntaxList(accessors.Where(a => a is not null)!))); + } + + protected override int GetTargetCaretPosition(ISyntaxFactsService syntaxFacts, SyntaxNode caretTarget, SourceText sourceText) + { + var propertyDeclaration = (PropertyDeclarationSyntax)caretTarget; + return propertyDeclaration.AccessorList!.CloseBraceToken.Span.End; + } + + protected override ImmutableArray GetPlaceHolderLocationsList(SyntaxNode node, ISyntaxFacts syntaxFacts, CancellationToken cancellationToken) + { + using var _ = ArrayBuilder.GetInstance(out var arrayBuilder); + var propertyDeclaration = (PropertyDeclarationSyntax)node; + var identifier = propertyDeclaration.Identifier; + var type = propertyDeclaration.Type; + + arrayBuilder.Add(new SnippetPlaceholder(identifier: type.ToString(), placeholderPositions: ImmutableArray.Create(type.SpanStart))); + arrayBuilder.Add(new SnippetPlaceholder(identifier: identifier.ValueText, placeholderPositions: ImmutableArray.Create(identifier.SpanStart))); + return arrayBuilder.ToImmutableArray(); + } + } +} diff --git a/src/Features/CSharp/Portable/Snippets/CSharpPropSnippetProvider.cs b/src/Features/CSharp/Portable/Snippets/CSharpPropSnippetProvider.cs index f592e2ce742f5..1b71cfd7d85b7 100644 --- a/src/Features/CSharp/Portable/Snippets/CSharpPropSnippetProvider.cs +++ b/src/Features/CSharp/Portable/Snippets/CSharpPropSnippetProvider.cs @@ -3,78 +3,24 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Composition; -using System.Text; -using Microsoft.CodeAnalysis.Shared.Extensions; -using System.Threading.Tasks; -using System.Threading; +using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Snippets; using Microsoft.CodeAnalysis.Snippets.SnippetProviders; -using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery; -using Microsoft.CodeAnalysis.Shared.Extensions.ContextQuery; -using Microsoft.CodeAnalysis.CSharp.Utilities; -using Microsoft.CodeAnalysis.Host.Mef; -using Roslyn.Utilities; -using System.Collections.Immutable; -using Microsoft.CodeAnalysis.Text; -using Microsoft.CodeAnalysis.Editing; -using Microsoft.CodeAnalysis.Shared.Utilities; -using Microsoft.CodeAnalysis.CSharp.Extensions; -using Microsoft.CodeAnalysis.LanguageService; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.CSharp.Snippets { [ExportSnippetProvider(nameof(ISnippetProvider), LanguageNames.CSharp), Shared] - internal sealed class CSharpPropSnippetProvider : AbstractPropSnippetProvider + internal sealed class CSharpPropSnippetProvider : AbstractCSharpAutoPropertySnippetProvider { - [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public CSharpPropSnippetProvider() { } - protected override async Task IsValidSnippetLocationAsync(Document document, int position, CancellationToken cancellationToken) - { - var syntaxTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); - Contract.ThrowIfNull(syntaxTree); - - return syntaxTree.IsMemberDeclarationContext(position, contextOpt: null, - SyntaxKindSet.AllMemberModifiers, SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, canBePartial: true, cancellationToken); - } + public override string Identifier => "prop"; - protected override async Task GenerateSnippetSyntaxAsync(Document document, int position, CancellationToken cancellationToken) - { - var compilation = await document.Project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); - var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - var generator = SyntaxGenerator.GetGenerator(document); - var identifierName = NameGenerator.GenerateUniqueName("MyProperty", - n => semanticModel.LookupSymbols(position, name: n).IsEmpty); - return generator.PropertyDeclaration( - name: identifierName, - type: compilation.GetSpecialType(SpecialType.System_Int32).GenerateTypeSyntax(allowVar: false), - accessibility: Accessibility.Public); - } - - protected override int GetTargetCaretPosition(ISyntaxFactsService syntaxFacts, SyntaxNode caretTarget, SourceText sourceText) - { - var propertyDeclaration = (PropertyDeclarationSyntax)caretTarget; - return propertyDeclaration.AccessorList!.CloseBraceToken.Span.End; - } - - protected override ImmutableArray GetPlaceHolderLocationsList(SyntaxNode node, ISyntaxFacts syntaxFacts, CancellationToken cancellationToken) - { - using var _ = ArrayBuilder.GetInstance(out var arrayBuilder); - var propertyDeclaration = (PropertyDeclarationSyntax)node; - var identifier = propertyDeclaration.Identifier; - var type = propertyDeclaration.Type; - - arrayBuilder.Add(new SnippetPlaceholder(identifier: type.ToString(), placeholderPositions: ImmutableArray.Create(type.SpanStart))); - arrayBuilder.Add(new SnippetPlaceholder(identifier: identifier.ValueText, placeholderPositions: ImmutableArray.Create(identifier.SpanStart))); - return arrayBuilder.ToImmutableArray(); - } + public override string Description => FeaturesResources.property_; } } diff --git a/src/Features/CSharp/Portable/Snippets/CSharpPropgSnippetProvider.cs b/src/Features/CSharp/Portable/Snippets/CSharpPropgSnippetProvider.cs new file mode 100644 index 0000000000000..ff1ab2c2e9161 --- /dev/null +++ b/src/Features/CSharp/Portable/Snippets/CSharpPropgSnippetProvider.cs @@ -0,0 +1,42 @@ +// 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.Composition; +using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Snippets; +using Microsoft.CodeAnalysis.Snippets.SnippetProviders; + +namespace Microsoft.CodeAnalysis.CSharp.Snippets +{ + [ExportSnippetProvider(nameof(ISnippetProvider), LanguageNames.CSharp), Shared] + internal class CSharpPropgSnippetProvider : AbstractCSharpAutoPropertySnippetProvider + { + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public CSharpPropgSnippetProvider() + { + } + + public override string Identifier => "propg"; + + public override string Description => FeaturesResources.get_only_property; + + protected override AccessorDeclarationSyntax? GenerateSetAccessorDeclaration(CSharpSyntaxContext syntaxContext, SyntaxGenerator generator) + { + // Interface cannot have properties with `private set` accessor. + // So if we are inside an interface, we just return null here. + // This causes the caller to just skip this `set` accessor + if (syntaxContext.ContainingTypeDeclaration is InterfaceDeclarationSyntax) + { + return null; + } + + return (AccessorDeclarationSyntax)generator.SetAccessorDeclaration(Accessibility.Private); + } + } +} diff --git a/src/Features/CSharp/Portable/Snippets/CSharpPropiSnippetProvider.cs b/src/Features/CSharp/Portable/Snippets/CSharpPropiSnippetProvider.cs new file mode 100644 index 0000000000000..20ffc714b1efc --- /dev/null +++ b/src/Features/CSharp/Portable/Snippets/CSharpPropiSnippetProvider.cs @@ -0,0 +1,32 @@ +// 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.Composition; +using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Snippets; +using Microsoft.CodeAnalysis.Snippets.SnippetProviders; + +namespace Microsoft.CodeAnalysis.CSharp.Snippets +{ + [ExportSnippetProvider(nameof(ISnippetProvider), LanguageNames.CSharp), Shared] + internal class CSharpPropiSnippetProvider : AbstractCSharpAutoPropertySnippetProvider + { + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public CSharpPropiSnippetProvider() + { + } + + public override string Identifier => "propi"; + + public override string Description => CSharpFeaturesResources.init_only_property; + + protected override AccessorDeclarationSyntax? GenerateSetAccessorDeclaration(CSharpSyntaxContext syntaxContext, SyntaxGenerator generator) + => SyntaxFactory.AccessorDeclaration(SyntaxKind.InitAccessorDeclaration).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)); + } +} diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf index a6ad57fce8269..aed887d0e2fc7 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf @@ -282,6 +282,11 @@ externí alias + + init-only property + init-only property + + <lambda expression> <lambda výraz> diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf index 0a111b4903b01..2a13d115c1274 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf @@ -282,6 +282,11 @@ externer Alias + + init-only property + init-only property + + <lambda expression> <Lambdaausdruck> diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf index 4557a09eda8ce..35b8e0cd832ef 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf @@ -282,6 +282,11 @@ alias externo + + init-only property + init-only property + + <lambda expression> <expresión lambda> diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf index 9aed46ba38503..3a7e4ae8f9968 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf @@ -282,6 +282,11 @@ alias externe + + init-only property + init-only property + + <lambda expression> <expression lambda> diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf index 71344adf88cb8..17e8b195a4bff 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf @@ -282,6 +282,11 @@ alias extern + + init-only property + init-only property + + <lambda expression> <espressione lambda> diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf index 2e8462fedbed8..f7a52ae29b582 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf @@ -282,6 +282,11 @@ extern エイリアス + + init-only property + init-only property + + <lambda expression> <ラムダ式> diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf index 914e6b3e8054d..b289b88f548a0 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf @@ -282,6 +282,11 @@ extern 별칭 + + init-only property + init-only property + + <lambda expression> <람다 식> diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf index b24a9128f4d42..0f76a512d0421 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf @@ -282,6 +282,11 @@ alias zewnętrzny + + init-only property + init-only property + + <lambda expression> <wyrażenie lambda> diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf index ed76b1ba03d4a..51355e0a09c1b 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf @@ -282,6 +282,11 @@ alias externo + + init-only property + init-only property + + <lambda expression> <expressão lambda> diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf index 4018387aea7ea..62e212d8d45c8 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf @@ -282,6 +282,11 @@ внешний псевдоним + + init-only property + init-only property + + <lambda expression> <лямбда-выражение> diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf index 2280b4a44d90d..562a625ce1e6a 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf @@ -282,6 +282,11 @@ dış diğer ad + + init-only property + init-only property + + <lambda expression> <lambda ifadesi> diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf index c4666685a7c52..d656fe4d39b2d 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf @@ -282,6 +282,11 @@ 外部别名 + + init-only property + init-only property + + <lambda expression> <lambda 表达式> diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf index 38312939a98be..e756d692150ad 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf @@ -282,6 +282,11 @@ 外部別名 + + init-only property + init-only property + + <lambda expression> <Lambda 運算式> diff --git a/src/Features/Core/Portable/FeaturesResources.resx b/src/Features/Core/Portable/FeaturesResources.resx index 2d8ac9097ea10..121237ade2117 100644 --- a/src/Features/Core/Portable/FeaturesResources.resx +++ b/src/Features/Core/Portable/FeaturesResources.resx @@ -3203,6 +3203,9 @@ Zero-width positive lookbehind assertions are typically used at the beginning of You must rename an identifier. + + get-only property + This {0} has {1} reference(s). diff --git a/src/Features/Core/Portable/Snippets/SnippetProviders/AbstractPropSnippetProvider.cs b/src/Features/Core/Portable/Snippets/SnippetProviders/AbstractPropertySnippetProvider.cs similarity index 87% rename from src/Features/Core/Portable/Snippets/SnippetProviders/AbstractPropSnippetProvider.cs rename to src/Features/Core/Portable/Snippets/SnippetProviders/AbstractPropertySnippetProvider.cs index e3605b38399e2..0e25000800f12 100644 --- a/src/Features/Core/Portable/Snippets/SnippetProviders/AbstractPropSnippetProvider.cs +++ b/src/Features/Core/Portable/Snippets/SnippetProviders/AbstractPropertySnippetProvider.cs @@ -11,12 +11,8 @@ namespace Microsoft.CodeAnalysis.Snippets.SnippetProviders { - internal abstract class AbstractPropSnippetProvider : AbstractSnippetProvider + internal abstract class AbstractPropertySnippetProvider : AbstractSnippetProvider { - public override string Identifier => "prop"; - - public override string Description => FeaturesResources.property_; - /// /// Generates the property syntax. /// Requires language specificity for the TypeSyntax as well as the diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf index bc31ff63f20d4..0669cd7d4a621 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf @@ -3562,6 +3562,11 @@ Specifikátor standardního formátu f představuje kombinaci vzorů dlouhého d obecná přetížení + + get-only property + get-only property + + if statement příkaz if diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf index 1a48783f68691..e3f405720990b 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf @@ -3562,6 +3562,11 @@ Der Standardformatbezeichner "f" repräsentiert eine Kombination aus den Mustern generische Überladungen + + get-only property + get-only property + + if statement Wenn-Anweisung diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf index ac25e0fb45ee3..6df1a95330dad 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf @@ -3562,6 +3562,11 @@ El especificador de formato estándar "f" representa una combinación de los pat sobrecargas genéricas + + get-only property + get-only property + + if statement instrucción if diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf index bc2e0af73cd44..8e715b1011fd1 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf @@ -3562,6 +3562,11 @@ Le spécificateur de format standard "f" représente une combinaison des modèle surcharges génériques + + get-only property + get-only property + + if statement si l’instruction diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf index 4b4c494c261fa..65b2790800857 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf @@ -3562,6 +3562,11 @@ L'identificatore di formato standard "f" rappresenta una combinazione degli sche overload generici + + get-only property + get-only property + + if statement istruzione if diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf index f74e9ae03870b..cacc9c41b5608 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf @@ -3562,6 +3562,11 @@ The "f" standard format specifier represents a combination of the long date ("D" ジェネリック オーバーロード + + get-only property + get-only property + + if statement If ステートメント diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf index c3b2e6d8b7e0d..031e1ab60e9ef 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf @@ -3562,6 +3562,11 @@ The "f" standard format specifier represents a combination of the long date ("D" 제네릭 오버로드 + + get-only property + get-only property + + if statement If 문 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf index 1450cf591c28b..cdf9f785dfdc3 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf @@ -3562,6 +3562,11 @@ Standardowy specyfikator formatu „f” reprezentuje połączenie wzorców dłu przeciążenia ogólne + + get-only property + get-only property + + if statement instrukcja if diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf index b61fa17ccbf12..9648ea78b3044 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf @@ -3562,6 +3562,11 @@ O especificador de formato padrão "f" representa uma combinação de padrões d sobrecargas genéricas + + get-only property + get-only property + + if statement instrução if diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf index 2a2ecf6812330..2de08fbb9e2db 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf @@ -3562,6 +3562,11 @@ The "f" standard format specifier represents a combination of the long date ("D" универсальные перегрузки + + get-only property + get-only property + + if statement оператор if diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf index 290eb85b69fd8..294f336ecfbe2 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf @@ -3562,6 +3562,11 @@ The "f" standard format specifier represents a combination of the long date ("D" genel aşırı yüklemeler + + get-only property + get-only property + + if statement If deyimi diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf index 038a482bee185..4d35c80c7bad6 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf @@ -3562,6 +3562,11 @@ The "f" standard format specifier represents a combination of the long date ("D" 多个泛型重载 + + get-only property + get-only property + + if statement If 语句 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf index 53995d6760947..f6f59b303dda8 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf @@ -3562,6 +3562,11 @@ The "f" standard format specifier represents a combination of the long date ("D" 泛型多載 + + get-only property + get-only property + + if statement If 陳述式