Skip to content

Commit

Permalink
Avoid automatically triggering completion for enum-like types
Browse files Browse the repository at this point in the history
  • Loading branch information
sharwell committed Mar 26, 2021
1 parent 0d2372a commit b252b30
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1172,7 +1172,9 @@ readonly struct Goo
";

await VerifyItemExistsAsync(markup + goo, "Goo.AMember", usePreviousCharAsTrigger: true);
await VerifyItemExistsAsync(markup + gooLike, "Goo.AMember", usePreviousCharAsTrigger: true);
await VerifyItemExistsAsync(markup + goo, "Goo.AMember", usePreviousCharAsTrigger: false);
await VerifyItemIsAbsentAsync(markup + gooLike, "Goo.AMember", usePreviousCharAsTrigger: true);
await VerifyItemExistsAsync(markup + gooLike, "Goo.AMember", usePreviousCharAsTrigger: false);
}

[Fact]
Expand Down Expand Up @@ -1207,7 +1209,9 @@ readonly struct Goo
";

await VerifyItemExistsAsync(markup + goo, "Goo.AMember", usePreviousCharAsTrigger: true);
await VerifyItemExistsAsync(markup + gooLike, "Goo.AMember", usePreviousCharAsTrigger: true);
await VerifyItemExistsAsync(markup + goo, "Goo.AMember", usePreviousCharAsTrigger: false);
await VerifyItemIsAbsentAsync(markup + gooLike, "Goo.AMember", usePreviousCharAsTrigger: true);
await VerifyItemExistsAsync(markup + gooLike, "Goo.AMember", usePreviousCharAsTrigger: false);
}

[Fact]
Expand Down Expand Up @@ -1246,7 +1250,9 @@ readonly struct E
";

await VerifyItemExistsAsync(e + markup, "E.A", usePreviousCharAsTrigger: true);
await VerifyItemExistsAsync(eLike + markup, "E.A", usePreviousCharAsTrigger: true);
await VerifyItemExistsAsync(e + markup, "E.A", usePreviousCharAsTrigger: false);
await VerifyItemIsAbsentAsync(eLike + markup, "E.A", usePreviousCharAsTrigger: true);
await VerifyItemExistsAsync(eLike + markup, "E.A", usePreviousCharAsTrigger: false);
}

[Theory]
Expand Down Expand Up @@ -1384,7 +1390,12 @@ struct DateTime
}}
";

await VerifyItemExistsAsync(markup, $"{typeName}.A", usePreviousCharAsTrigger: true);
if (typeName == nameof(DayOfWeek))
await VerifyItemExistsAsync(markup, $"{typeName}.A", usePreviousCharAsTrigger: true);
else
await VerifyItemIsAbsentAsync(markup, $"{typeName}.A", usePreviousCharAsTrigger: true);

await VerifyItemExistsAsync(markup, $"{typeName}.A", usePreviousCharAsTrigger: false);
}

[Theory]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1854,8 +1854,8 @@ public class C
<Document><![CDATA[
public class C
{
public C(object Alice, object Bob) { }
public C(object ignored) { }
public C(int Alice, int Bob) { }
public C(string ignored) { }

public void M()
{
Expand All @@ -1878,71 +1878,6 @@ public class C
End Using
End Function

<WpfTheory, CombinatorialData>
<Trait(Traits.Feature, Traits.Features.Completion)>
<WorkItem(28603, "https://github.com/dotnet/roslyn/issues/28603")>
Public Async Function TestImplicitObjectCreationExpression_WithSpace_EnumTypes(showCompletionInArgumentLists As Boolean) As Task
Using state = TestStateFactory.CreateCSharpTestState(
<Document><![CDATA[
using System;
public class C
{
public C(DayOfWeek Alice, DayOfWeek Bob) { }
public C(DateTimeKind ignored) { }

public void M()
{
C c = new$$
}
}]]></Document>, languageVersion:=LanguageVersion.CSharp9, showCompletionInArgumentLists:=showCompletionInArgumentLists)

state.SendTypeChars(" ")
Await state.AssertSelectedCompletionItem(displayText:="C", isHardSelected:=True)
state.SendTypeChars("(")

' The use of enum types causes completion to trigger automatically, even when
' showCompletionInArgumentLists is disabled.
Await state.AssertSignatureHelpSession()

state.SendTypeChars("A")
Await state.AssertSelectedCompletionItem(displayText:="Alice:", isHardSelected:=True)
state.SendTypeChars(":")
Assert.Contains("new C(Alice:", state.GetLineTextFromCaretPosition(), StringComparison.Ordinal)
End Using
End Function

<WpfTheory, CombinatorialData>
<Trait(Traits.Feature, Traits.Features.Completion)>
<WorkItem(28603, "https://github.com/dotnet/roslyn/issues/28603")>
Public Async Function TestImplicitObjectCreationExpression_WithSpace_EnumLikeTypes(showCompletionInArgumentLists As Boolean) As Task
Using state = TestStateFactory.CreateCSharpTestState(
<Document><![CDATA[
public class C
{
public C(int Alice, int Bob) { }
public C(string ignored) { }

public void M()
{
C c = new$$
}
}]]></Document>, languageVersion:=LanguageVersion.CSharp9, showCompletionInArgumentLists:=showCompletionInArgumentLists)

state.SendTypeChars(" ")
Await state.AssertSelectedCompletionItem(displayText:="C", isHardSelected:=True)
state.SendTypeChars("(")

' The use of enum-like types (int.MaxValue and string.Empty) causes completion to trigger automatically,
' even when showCompletionInArgumentLists is disabled.
Await state.AssertSignatureHelpSession()

state.SendTypeChars("A")
Await state.AssertSelectedCompletionItem(displayText:="Alice:", isHardSelected:=True)
state.SendTypeChars(":")
Assert.Contains("new C(Alice:", state.GetLineTextFromCaretPosition(), StringComparison.Ordinal)
End Using
End Function

<WpfTheory, CombinatorialData>
<Trait(Traits.Feature, Traits.Features.Completion)>
<WorkItem(13527, "https://github.com/dotnet/roslyn/issues/13527")>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ internal partial class EnumAndCompletionListTagCompletionProvider : LSPCompletio
.WithMatchPriority(MatchPriority.Preselect)
.WithSelectionBehavior(CompletionItemSelectionBehavior.HardSelection);

private static readonly ImmutableHashSet<char> s_triggerCharacters = ImmutableHashSet.Create(' ', '[', '(', '~');

[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public EnumAndCompletionListTagCompletionProvider()
Expand All @@ -58,7 +60,7 @@ public override bool IsInsertionTrigger(SourceText text, int characterPosition,
(options.GetOption(CompletionOptions.TriggerOnTypingLetters2, LanguageNames.CSharp) && CompletionUtilities.IsStartingNewWord(text, characterPosition));
}

public override ImmutableHashSet<char> TriggerCharacters { get; } = ImmutableHashSet.Create(' ', '[', '(', '~');
public override ImmutableHashSet<char> TriggerCharacters => s_triggerCharacters;

public override async Task ProvideCompletionsAsync(CompletionContext context)
{
Expand Down Expand Up @@ -128,6 +130,13 @@ private static async Task HandleSingleTypeAsync(CompletionContext context, Seman

if (enumType == null)
{
if (context.Trigger.Kind == CompletionTriggerKind.Insertion && s_triggerCharacters.Contains(context.Trigger.Character))
{
// This completion provider understands static members of matching types, but doesn't
// proactively trigger completion for them to avoid interfering with common typing patterns.
return;
}

// If this isn't an enum or marked with completionlist, also check if it contains static members of
// a matching type. These 'enum-like' types have similar characteristics to enum completion, but do
// not show the containing type as a separate item in completion.
Expand Down

0 comments on commit b252b30

Please sign in to comment.