diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/KeywordCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/KeywordCompletionProviderTests.cs index 0665acaab8aa7671b51373c47eee8c1a4c9fafe4..398d3d322ab0f246a9b2a15cbc3a6293648ce854 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/KeywordCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/KeywordCompletionProviderTests.cs @@ -317,5 +317,181 @@ public async Task FormattingAfterCompletionCommit_InSingleLineMethod() }"; await VerifyProviderCommitAsync(markupBeforeCommit, "return", expectedCodeAfterCommit, commitChar: ';', textTypedSoFar: "return"); } + + [Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.ReadonlyReferences)] + [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task ReadOnlyWillPopUpAfterRefInMethodParameterModifiers_Methods() + { + var code = @" +class Program +{ + public static void Test(ref $$ p) { } +}"; + + await VerifyItemExistsAsync(code, "readonly"); + } + + [Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.ReadonlyReferences)] + [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task ReadOnlyWillPopUpAfterRefInMethodParameterModifiers_SecondParameter() + { + var code = @" +class Program +{ + public static void Test(int p1, ref $$ p2) { } +}"; + + await VerifyItemExistsAsync(code, "readonly"); + } + + [Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.ReadonlyReferences)] + [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task ReadOnlyWillPopUpAfterRefInMethodParameterModifiers_Delegates() + { + var code = @" +public delegate int Delegate(ref $$ int p);"; + + await VerifyItemExistsAsync(code, "readonly"); + } + + [Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.ReadonlyReferences)] + [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task ReadOnlyWillPopUpAfterRefInMethodParameterModifiers_LocalFunctions() + { + var code = @" +class Program +{ + public static void Test() + { + void localFunc(ref $$ int p) { } + } +}"; + + await VerifyItemExistsAsync(code, "readonly"); + } + + [Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.ReadonlyReferences)] + [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task ReadOnlyWillPopUpAfterRefInMethodParameterModifiers_LambdaExpressions() + { + var code = @" +public delegate int Delegate(ref readonly int p); + +class Program +{ + public static void Test() + { + Delegate lambda = (ref $$ int p) => p; + } +}"; + + await VerifyItemExistsAsync(code, "readonly"); + } + + [Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.ReadonlyReferences)] + [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task ReadOnlyWillPopUpAfterRefInMethodParameterModifiers_AnonymousMethods() + { + var code = @" +public delegate int Delegate(ref readonly int p); + +class Program +{ + public static void Test() + { + Delegate anonymousDelegate = delegate (ref $$ int p) { return p; }; + } +}"; + + await VerifyItemExistsAsync(code, "readonly"); + } + + [Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.ReadonlyReferences)] + [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task InWillPopUpAsParameterModifier_Methods() + { + var code = @" +class Program +{ + public static void Test($$ p) { } +}"; + + await VerifyItemExistsAsync(code, "in"); + } + + [Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.ReadonlyReferences)] + [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task InWillPopUpAsParameterModifier_SecondParameter() + { + var code = @" +class Program +{ + public static void Test(int p1, $$ p2) { } +}"; + + await VerifyItemExistsAsync(code, "in"); + } + + [Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.ReadonlyReferences)] + [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task InWillPopUpAsParameterModifier_Delegates() + { + var code = @" +public delegate int Delegate($$ int p);"; + + await VerifyItemExistsAsync(code, "in"); + } + + [Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.ReadonlyReferences)] + [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task InWillPopUpAsParameterModifier_LocalFunctions() + { + var code = @" +class Program +{ + public static void Test() + { + void localFunc($$ int p) { } + } +}"; + + await VerifyItemExistsAsync(code, "in"); + } + + [Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.ReadonlyReferences)] + [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task InWillPopUpAsParameterModifier_LambdaExpressions() + { + var code = @" +public delegate int Delegate(ref readonly int p); + +class Program +{ + public static void Test() + { + Delegate lambda = ($$ int p) => p; + } +}"; + + await VerifyItemExistsAsync(code, "in"); + } + + [Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.ReadonlyReferences)] + [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task InWillPopUpAsParameterModifier_AnonymousMethods() + { + var code = @" +public delegate int Delegate(ref readonly int p); + +class Program +{ + public static void Test() + { + Delegate anonymousDelegate = delegate ($$ int p) { return p; }; + } +}"; + + await VerifyItemExistsAsync(code, "in"); + } } } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/InKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/InKeywordRecommender.cs index c7ec5f5d6b5f35262fa03e6a9c3e9f5dd2360301..5699c1dda7e9723e28263982fd1b77c006ee4edf 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/InKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/InKeywordRecommender.cs @@ -21,7 +21,10 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context IsValidContextInForEachClause(context) || IsValidContextInFromClause(context, cancellationToken) || IsValidContextInJoinClause(context, cancellationToken) || - context.TargetToken.IsTypeParameterVarianceContext(); + context.TargetToken.IsTypeParameterVarianceContext() || + context.SyntaxTree.IsParameterModifierContext(position, context.LeftToken, cancellationToken) || + context.SyntaxTree.IsAnonymousMethodParameterModifierContext(position, context.LeftToken, cancellationToken) || + context.SyntaxTree.IsPossibleLambdaParameterModifierContext(position, context.LeftToken, cancellationToken); } private bool IsValidContextInForEachClause(CSharpSyntaxContext context) diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ReadOnlyKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ReadOnlyKeywordRecommender.cs index 994e9413b5369893a06f6832b37c9e64644af6fc..3def52daab884cd695c2043325019cbc3bf13cad 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ReadOnlyKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ReadOnlyKeywordRecommender.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery; +using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders @@ -26,8 +27,11 @@ public ReadOnlyKeywordRecommender() protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { + var previousToken = context.LeftToken.GetPreviousTokenIfTouchingWord(position); + return context.IsGlobalStatementContext || + (previousToken.IsKind(SyntaxKind.RefKeyword) && previousToken.Parent.IsKind(SyntaxKind.Parameter, SyntaxKind.RefType)) || context.SyntaxTree.IsGlobalMemberDeclarationContext(context.Position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) || context.IsMemberDeclarationContext( validModifiers: s_validMemberModifiers, diff --git a/src/Workspaces/CSharp/Portable/Extensions/ContextQuery/SyntaxNodeExtensions.cs b/src/Workspaces/CSharp/Portable/Extensions/ContextQuery/SyntaxNodeExtensions.cs index 69af99f77d102544f89018f6f73e48bb1d6eb33a..f832fd8cac10bcd170059744101059c3fb3a5b8c 100644 --- a/src/Workspaces/CSharp/Portable/Extensions/ContextQuery/SyntaxNodeExtensions.cs +++ b/src/Workspaces/CSharp/Portable/Extensions/ContextQuery/SyntaxNodeExtensions.cs @@ -18,6 +18,7 @@ public static bool IsDelegateOrConstructorOrMethodParameterList(this SyntaxNode return node.IsParentKind(SyntaxKind.MethodDeclaration) || + node.IsParentKind(SyntaxKind.LocalFunctionStatement) || node.IsParentKind(SyntaxKind.ConstructorDeclaration) || node.IsParentKind(SyntaxKind.DelegateDeclaration); }