diff --git a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb index 5bda149147ed89e5a2a1680382c6fd4957ae65ee..709175329f8a570df9a051d25c0cbf0798fe2a07 100644 --- a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb @@ -576,6 +576,138 @@ class C End Using End Function + + + Public Async Function ColonInTupleNameInTupleLiteral() As Task + Using state = TestState.CreateCSharpTestState( + ) + + state.SendTypeChars("fi") + Await state.AssertSelectedCompletionItem(displayText:="first:", isHardSelected:=True) + Assert.Equal("first", state.CurrentCompletionPresenterSession.SelectedItem.FilterText) + state.SendTypeChars(":") + Assert.Contains("(first:", state.GetLineTextFromCaretPosition(), StringComparison.Ordinal) + End Using + End Function + + + + Public Async Function ColonInExactTupleNameInTupleLiteral() As Task + Using state = TestState.CreateCSharpTestState( + ) + + state.SendTypeChars("first") + Await state.AssertSelectedCompletionItem(displayText:="first:", isHardSelected:=True) + Assert.Equal("first", state.CurrentCompletionPresenterSession.SelectedItem.FilterText) + state.SendTypeChars(":") + Assert.Contains("(first:", state.GetLineTextFromCaretPosition(), StringComparison.Ordinal) + End Using + End Function + + + + Public Async Function ColonInTupleNameInTupleLiteralAfterComma() As Task + Using state = TestState.CreateCSharpTestState( + ) + + state.SendTypeChars("se") + Await state.AssertSelectedCompletionItem(displayText:="second:", isHardSelected:=True) + Assert.Equal("second", state.CurrentCompletionPresenterSession.SelectedItem.FilterText) + state.SendTypeChars(":") + Assert.Contains("(0, second:", state.GetLineTextFromCaretPosition(), StringComparison.Ordinal) + End Using + End Function + + + + Public Async Function TabInTupleNameInTupleLiteral() As Task + Using state = TestState.CreateCSharpTestState( + ) + + state.SendTypeChars("fi") + Await state.AssertSelectedCompletionItem(displayText:="first:", isHardSelected:=True) + Assert.Equal("first", state.CurrentCompletionPresenterSession.SelectedItem.FilterText) + state.SendTab() + state.SendTypeChars(":") + state.SendTypeChars("0") + Assert.Contains("(first:0", state.GetLineTextFromCaretPosition(), StringComparison.Ordinal) + End Using + End Function + + + + Public Async Function TabInExactTupleNameInTupleLiteral() As Task + Using state = TestState.CreateCSharpTestState( + ) + + state.SendTypeChars("first") + Await state.AssertSelectedCompletionItem(displayText:="first:", isHardSelected:=True) + Assert.Equal("first", state.CurrentCompletionPresenterSession.SelectedItem.FilterText) + state.SendTab() + state.SendTypeChars(":") + state.SendTypeChars("0") + Assert.Contains("(first:0", state.GetLineTextFromCaretPosition(), StringComparison.Ordinal) + End Using + End Function + + + + Public Async Function TabInTupleNameInTupleLiteralAfterComma() As Task + Using state = TestState.CreateCSharpTestState( + ) + + state.SendTypeChars("se") + Await state.AssertSelectedCompletionItem(displayText:="second:", isHardSelected:=True) + Assert.Equal("second", state.CurrentCompletionPresenterSession.SelectedItem.FilterText) + state.SendTab() + state.SendTypeChars(":") + state.SendTypeChars("1") + Assert.Contains("(0, second:1", state.GetLineTextFromCaretPosition(), StringComparison.Ordinal) + End Using + End Function + Public Async Function TestKeywordInTupleLiteral() As Task diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/TupleNameCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/TupleNameCompletionProvider.cs index cdc59e16a51a98da0fb381a4dd46d0a9e96d49aa..5f6e28fb619cbbc2fd3a6e07940719134ae94aa9 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/TupleNameCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/TupleNameCompletionProvider.cs @@ -2,20 +2,22 @@ using System.Collections.Immutable; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Completion; +using Microsoft.CodeAnalysis.Completion.Providers; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp.Completion.Providers { internal class TupleNameCompletionProvider : CommonCompletionProvider { - private static readonly CompletionItemRules _cachedRules = CompletionItemRules.Default - .WithCommitCharacterRule(CharacterSetModificationRule.Create(CharacterSetModificationKind.Remove, ':')); + private const string ColonString = ":"; public override async Task ProvideCompletionsAsync(CompletionContext completionContext) { @@ -40,7 +42,7 @@ public override async Task ProvideCompletionsAsync(CompletionContext completionC .Cast() .ToImmutableArray(); - AddItems(inferredTypes, index.Value, completionContext); + AddItems(inferredTypes, index.Value, completionContext, context.TargetToken.Parent.SpanStart); } private int? GetElementIndex(CSharpSyntaxContext context) @@ -65,7 +67,7 @@ public override async Task ProvideCompletionsAsync(CompletionContext completionC return null; } - private void AddItems(ImmutableArray inferredTypes, int index, CompletionContext context) + private void AddItems(ImmutableArray inferredTypes, int index, CompletionContext context, int spanStart) { foreach (var type in inferredTypes) { @@ -74,11 +76,26 @@ private void AddItems(ImmutableArray inferredTypes, int index, return; } + // Note: the filter text does not include the ':'. We want to ensure that if + // the user types the name exactly (up to the colon) that it is selected as an + // exact match. + var field = type.TupleElements[index]; - var item = CommonCompletionItem.Create( - field.Name + ":", _cachedRules, Glyph.FieldPublic); - context.AddItem(item); + + context.AddItem(SymbolCompletionItem.CreateWithSymbolId( + displayText: field.Name + ColonString, + symbols: ImmutableArray.Create(field), + rules: CompletionItemRules.Default, + contextPosition: spanStart, + filterText: field.Name)); } } + + protected override Task GetTextChangeAsync(CompletionItem selectedItem, char? ch, CancellationToken cancellationToken) + { + return Task.FromResult(new TextChange( + selectedItem.Span, + selectedItem.DisplayText.Substring(0, selectedItem.DisplayText.Length - ColonString.Length))); + } } } \ No newline at end of file