From 972fa47f184feba1cf6ceb22f24fea684f61baae Mon Sep 17 00:00:00 2001 From: Gen Lu Date: Thu, 18 Apr 2019 14:26:49 -0700 Subject: [PATCH 1/2] Fix sorting of import completion items --- .../TypeImportCompletionProviderTests.cs | 37 +++++++++++++++++++ .../Providers/TypeImportCompletionItem.cs | 5 ++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/TypeImportCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/TypeImportCompletionProviderTests.cs index 622fc70590562..7c4e55e907c7e 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/TypeImportCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/TypeImportCompletionProviderTests.cs @@ -646,6 +646,43 @@ class Bat await VerifyTypeImportItemIsAbsentAsync(markup, "Barr", inlineDescription: "Foo.Bar"); } + [InlineData(true)] + [InlineData(false)] + [Theory, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task TypesWithIdenticalNameButDifferentNamespaces(bool isProjectReference) + { + var file1 = $@" +namespace Foo +{{ + public class Bar + {{}} + + public class Bar + {{}} +}} +namespace Baz +{{ + public class Bar + {{}} + + public class Bar + {{}} +}}"; + var file2 = @" +namespace NS +{ + class C + { + $$ + } +}"; + var markup = GetMarkupWithReference(file2, file1, isProjectReference); + await VerifyTypeImportItemExistsAsync(markup, "Bar", glyph: (int)Glyph.ClassPublic, inlineDescription: "Foo"); + await VerifyTypeImportItemExistsAsync(markup, "Bar", displayTextSuffix: "<>", glyph: (int)Glyph.ClassPublic, inlineDescription: "Foo"); + await VerifyTypeImportItemExistsAsync(markup, "Bar", glyph: (int)Glyph.ClassPublic, inlineDescription: "Baz"); + await VerifyTypeImportItemExistsAsync(markup, "Bar", displayTextSuffix: "<>", glyph: (int)Glyph.ClassPublic, inlineDescription: "Baz"); + } + #endregion #region "Commit Change Tests" diff --git a/src/Features/Core/Portable/Completion/Providers/TypeImportCompletionItem.cs b/src/Features/Core/Portable/Completion/Providers/TypeImportCompletionItem.cs index 37b4dc05c64df..dceb206937857 100644 --- a/src/Features/Core/Portable/Completion/Providers/TypeImportCompletionItem.cs +++ b/src/Features/Core/Portable/Completion/Providers/TypeImportCompletionItem.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.Completion.Providers { internal static class TypeImportCompletionItem { - private const string SortTextFormat = "~{0}~{1}"; + private const string SortTextFormat = "~{0} {1}"; private const string GenericTypeNameManglingString = "`"; private static readonly string[] s_aritySuffixesOneToNine = { "`1", "`2", "`3", "`4", "`5", "`6", "`7", "`8", "`9" }; @@ -29,7 +29,8 @@ public static CompletionItem Create(INamedTypeSymbol typeSymbol, string containi // Hack: add tildes (ASCII: 126) to name and namespace as sort text: // 1. '~' before type name makes import items show after in-scope items - // 2. '~' before namespace makes types with identical type name but from different namespace all show up in the list + // 2. ' ' before namespace makes types with identical type name but from different namespace all show up in the list, + // it also makes sure type with shorter name shows first, e.g. 'SomeType` before 'SomeTypeWithLongerName'. var sortTextBuilder = PooledStringBuilder.GetInstance(); sortTextBuilder.Builder.AppendFormat(SortTextFormat, typeSymbol.Name, containingNamespace); From 14d59af9cdc053003d5776877f8ded95378a7674 Mon Sep 17 00:00:00 2001 From: Gen Lu Date: Mon, 22 Apr 2019 14:22:07 -0700 Subject: [PATCH 2/2] Add test to assert relative order of completion items --- .../TypeImportCompletionProviderTests.cs | 39 +++++++++++++++++++ .../AbstractCompletionProviderTests.cs | 10 +++++ 2 files changed, 49 insertions(+) diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/TypeImportCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/TypeImportCompletionProviderTests.cs index 7c4e55e907c7e..5a62935476bc0 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/TypeImportCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/TypeImportCompletionProviderTests.cs @@ -1,5 +1,8 @@ // 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.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.CSharp.Completion.Providers; @@ -843,6 +846,42 @@ class Bat await VerifyTypeImportItemIsAbsentAsync(markup, "Bar", inlineDescription: "Foo"); } + [Fact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task ShorterTypeNameShouldShowBeforeLongerTypeName() + { + var file1 = $@" +namespace Foo +{{ + public class SomeType + {{}} + public class SomeTypeWithLongerName + {{}} +}}"; + var file2 = @" +namespace Baz +{ + class Bat + { + $$ + } +}"; + var markup = CreateMarkupForSingleProject(file2, file1, LanguageNames.CSharp); + var completionList = await GetCompletionListAsync(markup).ConfigureAwait(false); + AssertRelativeOrder(new List() { "SomeType", "SomeTypeWithLongerName" }, completionList.Items); + } + + private static void AssertRelativeOrder(List expectedTypesInRelativeOrder, ImmutableArray allCompletionItems) + { + var hashset = new HashSet(expectedTypesInRelativeOrder); + var actualTypesInRelativeOrder = allCompletionItems.Where(item => hashset.Contains(item.DisplayText)).Select(item => item.DisplayText).ToImmutableArray(); + + Assert.Equal(expectedTypesInRelativeOrder.Count, actualTypesInRelativeOrder.Length); + for (var i = 0; i < expectedTypesInRelativeOrder.Count; ++i) + { + Assert.Equal(expectedTypesInRelativeOrder[i], actualTypesInRelativeOrder[i]); + } + } + private Task VerifyTypeImportItemExistsAsync(string markup, string expectedItem, int glyph, string inlineDescription, string displayTextSuffix = null) { return VerifyItemExistsAsync(markup, expectedItem, displayTextSuffix: displayTextSuffix, glyph: glyph, inlineDescription: inlineDescription); diff --git a/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs b/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs index 6ccec436286b6..7b4163e688eca 100644 --- a/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs +++ b/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs @@ -181,6 +181,16 @@ private Task VerifyAsync( matchingFilters, targetTypedExperimentEnabled); } + protected async Task GetCompletionListAsync(string markup) + { + var workspace = WorkspaceFixture.GetWorkspace(markup); + var currentDocument = workspace.CurrentSolution.GetDocument(WorkspaceFixture.CurrentDocument.Id); + var position = WorkspaceFixture.Position; + SetWorkspaceOptions(workspace); + + return await GetCompletionListAsync(GetCompletionService(workspace), currentDocument, position, CompletionTrigger.Invoke, workspace.Options).ConfigureAwait(false); + } + protected async Task VerifyCustomCommitProviderAsync(string markupBeforeCommit, string itemToCommit, string expectedCodeAfterCommit, SourceCodeKind? sourceCodeKind = null, char? commitChar = null) { using (WorkspaceFixture.GetWorkspace(markupBeforeCommit))