Skip to content

Commit

Permalink
Offer completion for #nullable enable|disable (#31171)
Browse files Browse the repository at this point in the history
  • Loading branch information
jcouv authored Nov 15, 2018
1 parent d5f7119 commit 7c04f24
Show file tree
Hide file tree
Showing 10 changed files with 250 additions and 2 deletions.
2 changes: 2 additions & 0 deletions src/Compilers/CSharp/Portable/Syntax/DirectiveTriviaSyntax.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ public SyntaxToken DirectiveNameToken
return ((LoadDirectiveTriviaSyntax)this).LoadKeyword;
case SyntaxKind.ShebangDirectiveTrivia:
return ((ShebangDirectiveTriviaSyntax)this).ExclamationToken;
case SyntaxKind.NullableDirectiveTrivia:
return ((NullableDirectiveTriviaSyntax)this).NullableKeyword;
default:
throw ExceptionUtilities.UnexpectedValue(this.Kind());
}
Expand Down
1 change: 1 addition & 0 deletions src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ public static bool IsPreprocessorDirective(SyntaxKind kind)
case SyntaxKind.LoadDirectiveTrivia:
case SyntaxKind.BadDirectiveTrivia:
case SyntaxKind.ShebangDirectiveTrivia:
case SyntaxKind.NullableDirectiveTrivia:
return true;
default:
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,9 @@ private void VerifyDirectivesSpecial(CSharpSyntaxNode node, params DirectiveInfo
Assert.Equal(exp.Text, setting.ValueText);
Assert.True(setting.Kind() == SyntaxKind.EnableKeyword || setting.Kind() == SyntaxKind.DisableKeyword);
}
Assert.Equal(SyntaxKind.NullableKeyword, nn.DirectiveNameToken.Kind());
Assert.True(SyntaxFacts.IsPreprocessorDirective(SyntaxKind.NullableDirectiveTrivia));
Assert.True(SyntaxFacts.IsPreprocessorKeyword(SyntaxKind.NullableKeyword));
break;
default:
if (null != exp.Text)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,31 @@

using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations
{
public class DisableKeywordRecommenderTests : KeywordRecommenderTests
{
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
[WorkItem(31130, "https://github.com/dotnet/roslyn/issues/31130")]
public async Task TestAfterNullable()
{
await VerifyKeywordAsync(@"#nullable $$");
}

[Fact]
[WorkItem(31130, "https://github.com/dotnet/roslyn/issues/31130")]
public async Task TestNotAfterNullableAndNewline()
{
await VerifyAbsenceAsync(@"
#nullable
$$
");
}


[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestNotAtRoot_Interactive()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations
{
[Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public class EnableKeywordRecommenderTests : KeywordRecommenderTests
{
[Fact]
[WorkItem(31130, "https://github.com/dotnet/roslyn/issues/31130")]
public async Task TestAfterNullable()
{
await VerifyKeywordAsync(@"#nullable $$");
}

[Fact]
[WorkItem(31130, "https://github.com/dotnet/roslyn/issues/31130")]
public async Task TestNotAfterNullableAndNewline()
{
await VerifyAbsenceAsync(@"
#nullable
$$
");
}

[Fact]
[WorkItem(31130, "https://github.com/dotnet/roslyn/issues/31130")]
public async Task TestNotAfterHash()
{
await VerifyAbsenceAsync(@"#$$");
}

[Fact]
public async Task TestNotAtRoot_Interactive()
{
await VerifyAbsenceAsync(SourceCodeKind.Script, @"$$");
}

[Fact]
public async Task TestNotAfterClass_Interactive()
{
await VerifyAbsenceAsync(SourceCodeKind.Script,
@"class C { }
$$");
}

[Fact]
public async Task TestNotAfterGlobalStatement_Interactive()
{
await VerifyAbsenceAsync(SourceCodeKind.Script,
@"System.Console.WriteLine();
$$");
}

[Fact]
public async Task TestNotAfterGlobalVariableDeclaration_Interactive()
{
await VerifyAbsenceAsync(SourceCodeKind.Script,
@"int i = 0;
$$");
}

[Fact]
public async Task TestNotInUsingAlias()
{
await VerifyAbsenceAsync(@"using Goo = $$");
}

[Fact]
public async Task TestNotInEmptyStatement()
{
await VerifyAbsenceAsync(AddInsideMethod(@"$$"));
}

[Fact]
public async Task TestNotAfterPragma()
{
await VerifyAbsenceAsync(@"#pragma $$");
}

[Fact]
public async Task TestNotAfterPragmaWarning()
{
await VerifyAbsenceAsync(@"#pragma warning $$");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations
{
[Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public class NullableKeywordRecommenderTests : KeywordRecommenderTests
{
[Fact]
public async Task TestNotAtRoot_Interactive()
{
await VerifyAbsenceAsync(SourceCodeKind.Script,
@"$$");
}

[Fact]
public async Task TestNotAfterClass_Interactive()
{
await VerifyAbsenceAsync(SourceCodeKind.Script,
@"class C { }
$$");
}

[Fact]
public async Task TestNotAfterGlobalStatement_Interactive()
{
await VerifyAbsenceAsync(SourceCodeKind.Script,
@"System.Console.WriteLine();
$$");
}

[Fact]
public async Task TestNotAfterGlobalVariableDeclaration_Interactive()
{
await VerifyAbsenceAsync(SourceCodeKind.Script,
@"int i = 0;
$$");
}

[Fact]
public async Task TestNotInUsingAlias()
{
await VerifyAbsenceAsync(
@"using Goo = $$");
}

[Fact]
public async Task TestNotInEmptyStatement()
{
await VerifyAbsenceAsync(AddInsideMethod(
@"$$"));
}

[Fact]
public async Task TestAfterHash()
{
await VerifyKeywordAsync(
@"#$$");
}

[Fact]
public async Task TestAfterHashAndSpace()
{
await VerifyKeywordAsync(
@"# $$");
}

[Fact]
public async Task TestNotAfterHashAndNullable()
{
await VerifyAbsenceAsync(
@"#nullable $$");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ private static ImmutableArray<IKeywordRecommender<CSharpSyntaxContext>> GetKeywo
new DynamicKeywordRecommender(),
new ElifKeywordRecommender(),
new ElseKeywordRecommender(),
new EnableKeywordRecommender(),
new EndIfKeywordRecommender(),
new EndRegionKeywordRecommender(),
new EnumKeywordRecommender(),
Expand Down Expand Up @@ -96,6 +97,7 @@ private static ImmutableArray<IKeywordRecommender<CSharpSyntaxContext>> GetKeywo
new NameOfKeywordRecommender(),
new NamespaceKeywordRecommender(),
new NewKeywordRecommender(),
new NullableKeywordRecommender(),
new NullKeywordRecommender(),
new ObjectKeywordRecommender(),
new OnKeywordRecommender(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,20 @@ public DisableKeywordRecommender()

protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
{
// # pragma warning |
// # pragma warning d|
var previousToken1 = context.TargetToken;
var previousToken2 = previousToken1.GetPreviousToken(includeSkipped: true);
var previousToken3 = previousToken2.GetPreviousToken(includeSkipped: true);

if (previousToken1.Kind() == SyntaxKind.NullableKeyword &&
previousToken2.Kind() == SyntaxKind.HashToken)
{
// # nullable |
// # nullable d|
return true;
}

// # pragma warning |
// # pragma warning d|
return
previousToken1.Kind() == SyntaxKind.WarningKeyword &&
previousToken2.Kind() == SyntaxKind.PragmaKeyword &&
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;

namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders
{
internal class EnableKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
{
public EnableKeywordRecommender()
: base(SyntaxKind.EnableKeyword, isValidInPreprocessorContext: true)
{
}

protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
{
var previousToken1 = context.TargetToken;
var previousToken2 = previousToken1.GetPreviousToken(includeSkipped: true);

// # nullable |
// # nullable e|
return previousToken1.Kind() == SyntaxKind.NullableKeyword &&
previousToken2.Kind() == SyntaxKind.HashToken;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;

namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders
{
internal class NullableKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
{
public NullableKeywordRecommender()
: base(SyntaxKind.NullableKeyword, isValidInPreprocessorContext: true)
{
}

protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
=> context.IsPreProcessorKeywordContext;
}
}

0 comments on commit 7c04f24

Please sign in to comment.