Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

F1 help for private protected and protected internal go to their expected places #46343

Merged
merged 4 commits into from
Jul 28, 2020
Merged
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 @@ -106,7 +106,7 @@ public override async Task<string> GetHelpTermAsync(Document document, TextSpan
return string.Empty;
}

private bool IsValid(SyntaxToken token, TextSpan span)
private static bool IsValid(SyntaxToken token, TextSpan span)
{
// If the token doesn't actually intersect with our position, give up
return token.Kind() == SyntaxKind.EndIfDirectiveTrivia || token.Span.IntersectsWith(span);
Expand All @@ -115,6 +115,7 @@ private bool IsValid(SyntaxToken token, TextSpan span)
private string TryGetText(SyntaxToken token, SemanticModel semanticModel, Document document, ISyntaxFactsService syntaxFacts, CancellationToken cancellationToken)
{
if (TryGetTextForContextualKeyword(token, out var text) ||
TryGetTextForCombinationKeyword(token, syntaxFacts, out text) ||
TryGetTextForKeyword(token, syntaxFacts, out text) ||
TryGetTextForPreProcessor(token, syntaxFacts, out text) ||
TryGetTextForSymbol(token, semanticModel, document, cancellationToken, out text) ||
Expand Down Expand Up @@ -153,9 +154,9 @@ private bool TryGetTextForSymbol(SyntaxToken token, SemanticModel semanticModel,
}

// Local: return the name if it's the declaration, otherwise the type
if (symbol is ILocalSymbol && !symbol.DeclaringSyntaxReferences.Any(d => d.GetSyntax().DescendantTokens().Contains(token)))
if (symbol is ILocalSymbol localSymbol && !symbol.DeclaringSyntaxReferences.Any(d => d.GetSyntax().DescendantTokens().Contains(token)))
{
symbol = ((ILocalSymbol)symbol).Type;
symbol = localSymbol.Type;
}

// Range variable: use the type
Expand All @@ -176,7 +177,7 @@ private bool TryGetTextForSymbol(SyntaxToken token, SemanticModel semanticModel,
return symbol != null;
}

private bool TryGetTextForOperator(SyntaxToken token, Document document, out string text)
private static bool TryGetTextForOperator(SyntaxToken token, Document document, out string text)
{
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
if (syntaxFacts.IsOperator(token) || syntaxFacts.IsPredefinedOperator(token) || SyntaxFacts.IsAssignmentExpressionOperatorToken(token.Kind()))
Expand Down Expand Up @@ -225,7 +226,7 @@ private bool TryGetTextForOperator(SyntaxToken token, Document document, out str
return false;
}

private bool TryGetTextForPreProcessor(SyntaxToken token, ISyntaxFactsService syntaxFacts, out string text)
private static bool TryGetTextForPreProcessor(SyntaxToken token, ISyntaxFactsService syntaxFacts, out string text)
{
if (syntaxFacts.IsPreprocessorKeyword(token))
{
Expand All @@ -243,7 +244,7 @@ private bool TryGetTextForPreProcessor(SyntaxToken token, ISyntaxFactsService sy
return false;
}

private bool TryGetTextForContextualKeyword(SyntaxToken token, out string text)
private static bool TryGetTextForContextualKeyword(SyntaxToken token, out string text)
{
if (token.Text == "nameof")
{
Expand Down Expand Up @@ -286,8 +287,31 @@ private bool TryGetTextForContextualKeyword(SyntaxToken token, out string text)
text = null;
return false;
}
private static bool TryGetTextForCombinationKeyword(SyntaxToken token, ISyntaxFactsService syntaxFacts, out string text)
{
switch (token.Kind())
{
case SyntaxKind.PrivateKeyword when ModifiersContains(token, syntaxFacts, SyntaxKind.ProtectedKeyword):
case SyntaxKind.ProtectedKeyword when ModifiersContains(token, syntaxFacts, SyntaxKind.PrivateKeyword):
text = "privateprotected_CSharpKeyword";
return true;

case SyntaxKind.ProtectedKeyword when ModifiersContains(token, syntaxFacts, SyntaxKind.InternalKeyword):
case SyntaxKind.InternalKeyword when ModifiersContains(token, syntaxFacts, SyntaxKind.ProtectedKeyword):
text = "protectedinternal_CSharpKeyword";
return true;
}

text = null;
return false;

static bool ModifiersContains(SyntaxToken token, ISyntaxFactsService syntaxFacts, SyntaxKind kind)
{
return syntaxFacts.GetModifiers(token.Parent).Any(t => t.IsKind(kind));
}
}

private bool TryGetTextForKeyword(SyntaxToken token, ISyntaxFactsService syntaxFacts, out string text)
private static bool TryGetTextForKeyword(SyntaxToken token, ISyntaxFactsService syntaxFacts, out string text)
{
if (token.Kind() == SyntaxKind.InKeyword)
{
Expand All @@ -311,7 +335,7 @@ private bool TryGetTextForKeyword(SyntaxToken token, ISyntaxFactsService syntaxF
}

if (token.ValueText == "var" && token.IsKind(SyntaxKind.IdentifierToken) &&
token.Parent.Parent is VariableDeclarationSyntax && token.Parent == ((VariableDeclarationSyntax)token.Parent.Parent).Type)
token.Parent.Parent is VariableDeclarationSyntax declaration && token.Parent == declaration.Type)
{
text = "var_CSharpKeyword";
return true;
Expand Down
105 changes: 103 additions & 2 deletions src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class F1HelpTests
private static readonly ComposableCatalog s_catalog = TestExportProvider.EntireAssemblyCatalogWithCSharpAndVisualBasic.WithPart(typeof(CSharpHelpContextService));
private static readonly IExportProviderFactory s_exportProviderFactory = ExportProviderCache.GetOrCreateExportProviderFactory(s_catalog);

private async Task TestAsync(string markup, string expectedText)
private static async Task TestAsync(string markup, string expectedText)
{
using var workspace = TestWorkspace.CreateCSharp(markup, exportProvider: s_exportProviderFactory.CreateExportProvider());
var caret = workspace.Documents.First().CursorPosition;
Expand All @@ -34,11 +34,112 @@ private async Task TestAsync(string markup, string expectedText)
Assert.Equal(expectedText, actualText);
}

private async Task Test_KeywordAsync(string markup, string expectedText)
private static async Task Test_KeywordAsync(string markup, string expectedText)
{
await TestAsync(markup, expectedText + "_CSharpKeyword");
}

[Fact, Trait(Traits.Feature, Traits.Features.F1Help)]
public async Task TestInternal()
{
await Test_KeywordAsync(
@"intern[||]al class C
{
}", "internal");
}

[Fact, Trait(Traits.Feature, Traits.Features.F1Help)]
public async Task TestProtected()
{
await Test_KeywordAsync(
@"public class C
{
protec[||]ted void goo();
}", "protected");
}

[Fact, Trait(Traits.Feature, Traits.Features.F1Help)]
public async Task TestProtectedInternal1()
{
await Test_KeywordAsync(
@"public class C
{
internal protec[||]ted void goo();
}", "protectedinternal");
}

[Fact, Trait(Traits.Feature, Traits.Features.F1Help)]
public async Task TestProtectedInternal2()
{
await Test_KeywordAsync(
@"public class C
{
protec[||]ted internal void goo();
}", "protectedinternal");
}

[Fact, Trait(Traits.Feature, Traits.Features.F1Help)]
public async Task TestPrivateProtected1()
{
await Test_KeywordAsync(
@"public class C
{
private protec[||]ted void goo();
}", "privateprotected");
}

[Fact, Trait(Traits.Feature, Traits.Features.F1Help)]
public async Task TestPrivateProtected2()
{
await Test_KeywordAsync(
@"public class C
{
priv[||]ate protected void goo();
}", "privateprotected");
}

[Fact, Trait(Traits.Feature, Traits.Features.F1Help)]
public async Task TestPrivateProtected3()
{
await Test_KeywordAsync(
@"public class C
{
protected priv[||]ate void goo();
}", "privateprotected");
}

[Fact, Trait(Traits.Feature, Traits.Features.F1Help)]
public async Task TestPrivateProtected4()
{
await Test_KeywordAsync(
@"public class C
{
prot[||]ected private void goo();
}", "privateprotected");
}

[Fact, Trait(Traits.Feature, Traits.Features.F1Help)]
public async Task TestModifierSoup()
{
await Test_KeywordAsync(
@"public class C
{
private new prot[||]ected static unsafe void foo()
{
}
}", "privateprotected");
}

[Fact, Trait(Traits.Feature, Traits.Features.F1Help)]
public async Task TestModifierSoupField()
{
await Test_KeywordAsync(
@"public class C
{
new prot[||]ected static unsafe private goo;
}", "privateprotected");
}

[Fact, Trait(Traits.Feature, Traits.Features.F1Help)]
public async Task TestVoid()
{
Expand Down
109 changes: 108 additions & 1 deletion src/VisualStudio/Core/Test/Help/HelpTests.vb
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,121 @@ Imports Roslyn.Utilities
Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Help
<[UseExportProvider]>
Public Class HelpTests
Public Async Function TestAsync(markup As String, expected As String) As Tasks.Task
Public Shared Async Function TestAsync(markup As String, expected As String) As Tasks.Task
Using workspace = TestWorkspace.CreateVisualBasic(markup)
Dim caret = workspace.Documents.First().CursorPosition
Dim service = New VisualBasicHelpContextService()
Assert.Equal(expected, Await service.GetHelpTermAsync(workspace.CurrentSolution.Projects.First().Documents.First(), workspace.Documents.First().SelectedSpans.First(), CancellationToken.None))
End Using
End Function

<Fact, Trait(Traits.Feature, Traits.Features.F1Help)>
Public Async Function TestFriend() As Task
Dim text = <a>
Fri[||]end Class G
End Class</a>

Await TestAsync(text.Value, "vb.Friend")
End Function

<Fact, Trait(Traits.Feature, Traits.Features.F1Help)>
Public Async Function TestProtected() As Task
Dim text = <a>
Public Class G
Protec[||]ted Sub M()
End Sub
End Class</a>

Await TestAsync(text.Value, "vb.Protected")
End Function

<Fact, Trait(Traits.Feature, Traits.Features.F1Help)>
Public Async Function TestProtectedFriend1() As Task
Dim text = <a>
Public Class G
Protec[||]ted Friend Sub M()
End Sub
End Class</a>

Await TestAsync(text.Value, "vb.ProtectedFriend")
End Function

<Fact, Trait(Traits.Feature, Traits.Features.F1Help)>
Public Async Function TestProtectedFriend2() As Task
Dim text = <a>
Public Class G
Friend Protec[||]ted Sub M()
End Sub
End Class</a>

Await TestAsync(text.Value, "vb.ProtectedFriend")
End Function

<Fact, Trait(Traits.Feature, Traits.Features.F1Help)>
Public Async Function TestPrivateProtected1() As Task
Dim text = <a>
Public Class G
Private Protec[||]ted Sub M()
End Sub
End Class</a>

Await TestAsync(text.Value, "vb.PrivateProtected")
End Function

<Fact, Trait(Traits.Feature, Traits.Features.F1Help)>
Public Async Function TestPrivateProtected2() As Task
Dim text = <a>
Public Class G
Priv[||]ate Protected Sub M()
End Sub
End Class</a>

Await TestAsync(text.Value, "vb.PrivateProtected")
End Function

<Fact, Trait(Traits.Feature, Traits.Features.F1Help)>
Public Async Function TestPrivateProtected3() As Task
Dim text = <a>
Public Class G
Protected Priv[||]ate Sub M()
End Sub
End Class</a>

Await TestAsync(text.Value, "vb.PrivateProtected")
End Function

<Fact, Trait(Traits.Feature, Traits.Features.F1Help)>
Public Async Function TestPrivateProtected4() As Task
Dim text = <a>
Public Class G
Protec[||]ted Private Sub M()
End Sub
End Class</a>

Await TestAsync(text.Value, "vb.PrivateProtected")
End Function

<Fact, Trait(Traits.Feature, Traits.Features.F1Help)>
Public Async Function TestModifierSoup() As Task
Dim text = <a>
Public Class G
Protec[||]ted Async Shared Private Sub M()
End Sub
End Class</a>

Await TestAsync(text.Value, "vb.PrivateProtected")
End Function

<Fact, Trait(Traits.Feature, Traits.Features.F1Help)>
Public Async Function TestModifierSoupField() As Task
Dim text = <a>
Public Class G
Private Shadows Shared Prot[||]ected foo as Boolean
End Class</a>

Await TestAsync(text.Value, "vb.PrivateProtected")
End Function

<Fact, Trait(Traits.Feature, Traits.Features.F1Help)>
Public Async Function TestAddHandler1() As Task
Dim text = <a>
Expand Down
2 changes: 2 additions & 0 deletions src/VisualStudio/VisualBasic/Impl/Help/HelpKeywords.vb
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Help
Friend Const Iterator As String = "vb.Iterator"
Friend Const Await As String = "vb.Await"
Friend Const Yield As String = "vb.Yield"
Friend Const PrivateProtected As String = "vb.PrivateProtected"
Friend Const ProtectedFriend As String = "vb.ProtectedFriend"

End Class

Expand Down
Loading