Skip to content

Commit

Permalink
Merge pull request #35120 from genlu/CompletionSorting
Browse files Browse the repository at this point in the history
Fix sorting of import completion items
  • Loading branch information
genlu authored Apr 22, 2019
2 parents dcf019c + 14d59af commit 88a9c2e
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -646,6 +649,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<T>
{{}}
}}
namespace Baz
{{
public class Bar<T>
{{}}
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"
Expand Down Expand Up @@ -806,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<string>() { "SomeType", "SomeTypeWithLongerName" }, completionList.Items);
}

private static void AssertRelativeOrder(List<string> expectedTypesInRelativeOrder, ImmutableArray<CompletionItem> allCompletionItems)
{
var hashset = new HashSet<string>(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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,16 @@ private Task VerifyAsync(
matchingFilters, targetTypedExperimentEnabled);
}

protected async Task<RoslynCompletion.CompletionList> 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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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" };

Expand All @@ -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);

Expand Down

0 comments on commit 88a9c2e

Please sign in to comment.