diff --git a/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.ProgressAdapter.cs b/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.ProgressAdapter.cs index 89e540203dc6c..2563ced0e5054 100644 --- a/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.ProgressAdapter.cs +++ b/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.ProgressAdapter.cs @@ -102,7 +102,11 @@ private async Task GetDefinitionItemAsync(ISymbol definition) if (!_definitionToItem.TryGetValue(definition, out var definitionItem)) { definitionItem = await definition.ToClassifiedDefinitionItemAsync( - _solution, includeHiddenLocations: false, _options, _context.CancellationToken).ConfigureAwait(false); + _solution, + isPrimary: _definitionToItem.Count == 0, + includeHiddenLocations: false, + _options, + _context.CancellationToken).ConfigureAwait(false); _definitionToItem[definition] = definitionItem; } diff --git a/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.SymbolMoniker.cs b/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.SymbolMoniker.cs index fef07234a43e9..e8406acfc7af7 100644 --- a/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.SymbolMoniker.cs +++ b/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.SymbolMoniker.cs @@ -28,9 +28,6 @@ private static async Task FindSymbolMonikerReferencesAsync( if (moniker == null) return; - // Let the find-refs window know we have outstanding work - await using var _ = await context.ProgressTracker.AddSingleItemAsync().ConfigureAwait(false); - var displayParts = GetDisplayParts(definition).AddRange(new[] { new TaggedText(TextTags.Space, " "), @@ -45,8 +42,7 @@ private static async Task FindSymbolMonikerReferencesAsync( var monikers = ImmutableArray.Create(moniker); var first = true; - await foreach (var referenceItem in monikerUsagesService.FindReferencesByMoniker( - definitionItem, monikers, context.ProgressTracker, cancellationToken)) + await foreach (var referenceItem in monikerUsagesService.FindReferencesByMoniker(definitionItem, monikers, cancellationToken)) { if (first) { diff --git a/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.cs b/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.cs index a4f2849fa3610..6848cdbbb1491 100644 --- a/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.cs +++ b/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.cs @@ -52,7 +52,7 @@ await context.SetSearchTitleAsync( foreach (var implementation in implementations) { var definitionItem = await implementation.ToClassifiedDefinitionItemAsync( - solution, includeHiddenLocations: false, FindReferencesSearchOptions.Default, cancellationToken).ConfigureAwait(false); + solution, isPrimary: true, includeHiddenLocations: false, FindReferencesSearchOptions.Default, cancellationToken).ConfigureAwait(false); await context.OnDefinitionFoundAsync(definitionItem).ConfigureAwait(false); } diff --git a/src/EditorFeatures/Core/FindUsages/IDefinitionsAndReferencesFactory.cs b/src/EditorFeatures/Core/FindUsages/IDefinitionsAndReferencesFactory.cs index 90c1c8dca826b..b236061373a51 100644 --- a/src/EditorFeatures/Core/FindUsages/IDefinitionsAndReferencesFactory.cs +++ b/src/EditorFeatures/Core/FindUsages/IDefinitionsAndReferencesFactory.cs @@ -61,18 +61,20 @@ public static DefinitionItem ToNonClassifiedDefinitionItem( // to compute the classified spans for the locations of the definition. So it's totally // fine to pass in CancellationToken.None and block on the result. return ToDefinitionItemAsync( - definition, solution, includeHiddenLocations, includeClassifiedSpans: false, + definition, solution, isPrimary: false, includeHiddenLocations, includeClassifiedSpans: false, options: FindReferencesSearchOptions.Default, cancellationToken: CancellationToken.None).WaitAndGetResult_CanCallOnBackground(CancellationToken.None); } public static Task ToClassifiedDefinitionItemAsync( this ISymbol definition, Solution solution, + bool isPrimary, bool includeHiddenLocations, FindReferencesSearchOptions options, CancellationToken cancellationToken) { - return ToDefinitionItemAsync(definition, solution, + return ToDefinitionItemAsync( + definition, solution, isPrimary, includeHiddenLocations, includeClassifiedSpans: true, options, cancellationToken); } @@ -80,6 +82,7 @@ public static Task ToClassifiedDefinitionItemAsync( private static async Task ToDefinitionItemAsync( this ISymbol definition, Solution solution, + bool isPrimary, bool includeHiddenLocations, bool includeClassifiedSpans, FindReferencesSearchOptions options, @@ -107,7 +110,7 @@ private static async Task ToDefinitionItemAsync( using var sourceLocationsDisposer = ArrayBuilder.GetInstance(out var sourceLocations); - var properties = GetProperties(definition); + var properties = GetProperties(definition, isPrimary); var displayableProperties = AbstractReferenceFinder.GetAdditionalFindUsagesProperties(definition); @@ -161,10 +164,15 @@ private static async Task ToDefinitionItemAsync( nameDisplayParts, properties, displayableProperties, displayIfNoReferences); } - private static ImmutableDictionary GetProperties(ISymbol definition) + private static ImmutableDictionary GetProperties(ISymbol definition, bool isPrimary) { var properties = ImmutableDictionary.Empty; + if (isPrimary) + { + properties = properties.Add(DefinitionItem.Primary, ""); + } + var rqName = RQNameInternal.From(definition); if (rqName != null) { diff --git a/src/EditorFeatures/Core/FindUsages/IFindSymbolMonikerUsagesService.cs b/src/EditorFeatures/Core/FindUsages/IFindSymbolMonikerUsagesService.cs index d7b6431e5b099..58e73803e64ef 100644 --- a/src/EditorFeatures/Core/FindUsages/IFindSymbolMonikerUsagesService.cs +++ b/src/EditorFeatures/Core/FindUsages/IFindSymbolMonikerUsagesService.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.FindUsages; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Utilities; namespace Microsoft.CodeAnalysis.Editor.FindUsages @@ -23,20 +22,20 @@ namespace Microsoft.CodeAnalysis.Editor.FindUsages /// internal interface IFindSymbolMonikerUsagesService : IWorkspaceService { - IAsyncEnumerable FindReferencesByMoniker(DefinitionItem definition, ImmutableArray monikers, IStreamingProgressTracker progress, CancellationToken cancellationToken); - IAsyncEnumerable FindDefinitionsByMoniker(SymbolMoniker moniker, IStreamingProgressTracker progress, CancellationToken cancellationToken); - IAsyncEnumerable FindImplementationsByMoniker(SymbolMoniker moniker, IStreamingProgressTracker progress, CancellationToken cancellationToken); + IAsyncEnumerable FindReferencesByMoniker(DefinitionItem definition, ImmutableArray monikers, CancellationToken cancellationToken); + IAsyncEnumerable FindDefinitionsByMoniker(SymbolMoniker moniker, CancellationToken cancellationToken); + IAsyncEnumerable FindImplementationsByMoniker(SymbolMoniker moniker, CancellationToken cancellationToken); } internal abstract class AbstractFindSymbolMonikerUsagesService : IFindSymbolMonikerUsagesService { - public virtual IAsyncEnumerable FindDefinitionsByMoniker(SymbolMoniker moniker, IStreamingProgressTracker progress, CancellationToken cancellationToken) + public virtual IAsyncEnumerable FindDefinitionsByMoniker(SymbolMoniker moniker, CancellationToken cancellationToken) => EmptyAsyncEnumerable.Instance; - public virtual IAsyncEnumerable FindImplementationsByMoniker(SymbolMoniker moniker, IStreamingProgressTracker progress, CancellationToken cancellationToken) + public virtual IAsyncEnumerable FindImplementationsByMoniker(SymbolMoniker moniker, CancellationToken cancellationToken) => EmptyAsyncEnumerable.Instance; - public virtual IAsyncEnumerable FindReferencesByMoniker(DefinitionItem definition, ImmutableArray monikers, IStreamingProgressTracker progress, CancellationToken cancellationToken) + public virtual IAsyncEnumerable FindReferencesByMoniker(DefinitionItem definition, ImmutableArray monikers, CancellationToken cancellationToken) => EmptyAsyncEnumerable.Instance; } diff --git a/src/EditorFeatures/Core/GoToBase/AbstractGoToBaseService.cs b/src/EditorFeatures/Core/GoToBase/AbstractGoToBaseService.cs index f6ece802cf919..892940287382d 100644 --- a/src/EditorFeatures/Core/GoToBase/AbstractGoToBaseService.cs +++ b/src/EditorFeatures/Core/GoToBase/AbstractGoToBaseService.cs @@ -47,7 +47,7 @@ await context.SetSearchTitleAsync( if (sourceDefinition != null) { var definitionItem = await sourceDefinition.ToClassifiedDefinitionItemAsync( - solution, includeHiddenLocations: false, FindReferencesSearchOptions.Default, cancellationToken: cancellationToken).ConfigureAwait(false); + solution, isPrimary: true, includeHiddenLocations: false, FindReferencesSearchOptions.Default, cancellationToken: cancellationToken).ConfigureAwait(false); await context.OnDefinitionFoundAsync(definitionItem).ConfigureAwait(false); found = true; diff --git a/src/Features/Core/Portable/FindUsages/DefinitionItem.cs b/src/Features/Core/Portable/FindUsages/DefinitionItem.cs index b648e259c7147..1f9182f781419 100644 --- a/src/Features/Core/Portable/FindUsages/DefinitionItem.cs +++ b/src/Features/Core/Portable/FindUsages/DefinitionItem.cs @@ -21,6 +21,12 @@ namespace Microsoft.CodeAnalysis.FindUsages /// internal abstract partial class DefinitionItem { + /// + /// The definition item corresponding to the initial symbol the user was trying to find. This item should get + /// prominent placement in the final UI for the user. + /// + internal const string Primary = nameof(Primary); + // Existing behavior is to do up to two lookups for 3rd party navigation for FAR. One // for the symbol itself and one for a 'fallback' symbol. For example, if we're FARing // on a constructor, then the fallback symbol will be the actual type that the constructor diff --git a/src/VisualStudio/Core/Def/Implementation/FindReferences/Contexts/AbstractTableDataSourceFindUsagesContext.cs b/src/VisualStudio/Core/Def/Implementation/FindReferences/Contexts/AbstractTableDataSourceFindUsagesContext.cs index e54124749a921..04bf1cd5b0989 100644 --- a/src/VisualStudio/Core/Def/Implementation/FindReferences/Contexts/AbstractTableDataSourceFindUsagesContext.cs +++ b/src/VisualStudio/Core/Def/Implementation/FindReferences/Contexts/AbstractTableDataSourceFindUsagesContext.cs @@ -382,7 +382,7 @@ protected RoslynDefinitionBucket GetOrCreateDefinitionBucket(DefinitionItem defi { if (!_definitionToBucket.TryGetValue(definition, out var bucket)) { - bucket = new RoslynDefinitionBucket(Presenter, this, definition); + bucket = RoslynDefinitionBucket.Create(Presenter, this, definition); _definitionToBucket.Add(definition, bucket); } diff --git a/src/VisualStudio/Core/Def/Implementation/FindReferences/RoslynDefinitionBucket.cs b/src/VisualStudio/Core/Def/Implementation/FindReferences/RoslynDefinitionBucket.cs index 2f3d2b5a69091..a379d42ad49c0 100644 --- a/src/VisualStudio/Core/Def/Implementation/FindReferences/RoslynDefinitionBucket.cs +++ b/src/VisualStudio/Core/Def/Implementation/FindReferences/RoslynDefinitionBucket.cs @@ -24,17 +24,34 @@ private class RoslynDefinitionBucket : DefinitionBucket, ISupportsNavigation public readonly DefinitionItem DefinitionItem; public RoslynDefinitionBucket( + string name, + bool expandedByDefault, StreamingFindUsagesPresenter presenter, AbstractTableDataSourceFindUsagesContext context, DefinitionItem definitionItem) - : base(name: definitionItem.DisplayParts.JoinText() + " " + definitionItem.GetHashCode(), + : base(name, sourceTypeIdentifier: context.SourceTypeIdentifier, - identifier: context.Identifier) + identifier: context.Identifier, + expandedByDefault: expandedByDefault) { _presenter = presenter; DefinitionItem = definitionItem; } + public static RoslynDefinitionBucket Create( + StreamingFindUsagesPresenter presenter, + AbstractTableDataSourceFindUsagesContext context, + DefinitionItem definitionItem) + { + var isPrimary = definitionItem.Properties.ContainsKey(DefinitionItem.Primary); + + // Sort the primary item above everything else. + var name = $"{(isPrimary ? 0 : 1)} {definitionItem.DisplayParts.JoinText()} {definitionItem.GetHashCode()}"; + + return new RoslynDefinitionBucket( + name, expandedByDefault: true, presenter, context, definitionItem); + } + public bool TryNavigateTo(bool isPreview) => DefinitionItem.TryNavigateTo(_presenter._workspace, isPreview); diff --git a/src/VisualStudio/Core/Def/Implementation/FindUsages/VisualStudioFindSymbolMonikerUsagesService.cs b/src/VisualStudio/Core/Def/Implementation/FindUsages/VisualStudioFindSymbolMonikerUsagesService.cs index 34a0d78afc468..a5805673c05a3 100644 --- a/src/VisualStudio/Core/Def/Implementation/FindUsages/VisualStudioFindSymbolMonikerUsagesService.cs +++ b/src/VisualStudio/Core/Def/Implementation/FindUsages/VisualStudioFindSymbolMonikerUsagesService.cs @@ -43,18 +43,19 @@ public VisualStudioFindSymbolMonikerUsagesService( } public override async IAsyncEnumerable FindReferencesByMoniker( - DefinitionItem definition, ImmutableArray monikers, - IStreamingProgressTracker progress, [EnumeratorCancellation] CancellationToken cancellationToken) + DefinitionItem definition, ImmutableArray monikers, [EnumeratorCancellation] CancellationToken cancellationToken) { if (_codeIndexProvider == null) yield break; + // Only grab the first 500 results. This keeps server load lower and is acceptable for //build demo purposes. + const int PageCount = 5; + var convertedMonikers = ConvertMonikers(monikers); - var currentPage = 0; - while (true) + for (var currentPage = 0; currentPage < PageCount; currentPage++) { var referenceItems = await FindReferencesByMonikerAsync( - _codeIndexProvider, definition, convertedMonikers, progress, currentPage, cancellationToken).ConfigureAwait(false); + _codeIndexProvider, definition, convertedMonikers, currentPage, cancellationToken).ConfigureAwait(false); // If we got no items, we're done. if (referenceItems.Length == 0) @@ -62,24 +63,17 @@ public override async IAsyncEnumerable FindReferencesByMo foreach (var item in referenceItems) yield return item; - - // Otherwise, we got some items. Return them to our caller and attempt to retrieve - // another page. - currentPage++; } } private async Task> FindReferencesByMonikerAsync( - ICodeIndexProvider codeIndexProvider, DefinitionItem definition, ImmutableArray monikers, - IStreamingProgressTracker progress, int pageIndex, CancellationToken cancellationToken) + ICodeIndexProvider codeIndexProvider, DefinitionItem definition, + ImmutableArray monikers, int pageIndex, CancellationToken cancellationToken) { - // Let the find-refs window know we have outstanding work - await using var _1 = await progress.AddSingleItemAsync().ConfigureAwait(false); - var results = await FindReferencesByMonikerAsync( codeIndexProvider, monikers, pageIndex, cancellationToken).ConfigureAwait(false); - using var _2 = ArrayBuilder.GetInstance(out var referenceItems); + using var _ = ArrayBuilder.GetInstance(out var referenceItems); foreach (var result in results) referenceItems.Add(ConvertResult(definition, result));