diff --git a/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.cs b/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.cs index 2c6157b396c775d5aa706d22663969797d08d4b7..ae61b9445efa1f633d15de8026f1d52985c1e54d 100644 --- a/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.cs +++ b/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using System; using System.Collections.Immutable; using System.Linq; @@ -12,6 +14,7 @@ using Microsoft.CodeAnalysis.FindUsages; using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.SymbolMonikers; using Roslyn.Utilities; using VS.IntelliNav.Contracts; @@ -20,10 +23,14 @@ namespace Microsoft.CodeAnalysis.Editor.FindUsages internal abstract partial class AbstractFindUsagesService : IFindUsagesService { private readonly IThreadingContext _threadingContext; + private readonly ICodeIndexProvider? _codeIndexProvider; - protected AbstractFindUsagesService(IThreadingContext threadingContext) + protected AbstractFindUsagesService( + IThreadingContext threadingContext, + ICodeIndexProvider? codeIndexProvider) { _threadingContext = threadingContext; + _codeIndexProvider = codeIndexProvider; } public async Task FindImplementationsAsync( @@ -111,7 +118,7 @@ protected AbstractFindUsagesService(IThreadingContext threadingContext) ImmutableArray definitions, CancellationToken cancellationToken) { - var factory = solution.Workspace.Services.GetService(); + var factory = solution.Workspace.Services.GetRequiredService(); return definitions.Select(d => factory.GetThirdPartyDefinitionItem(solution, d, cancellationToken)) .WhereNotNull() .ToImmutableArray(); @@ -127,13 +134,11 @@ protected AbstractFindUsagesService(IThreadingContext threadingContext) var symbolAndProject = await FindUsagesHelpers.GetRelevantSymbolAndProjectAtPositionAsync( document, position, cancellationToken).ConfigureAwait(false); if (symbolAndProject == null) - { return; - } await FindSymbolReferencesAsync( - _threadingContext, - context, symbolAndProject?.symbol, symbolAndProject?.project, cancellationToken).ConfigureAwait(false); + _threadingContext, _codeIndexProvider, context, + symbolAndProject.Value.symbol, symbolAndProject.Value.project, cancellationToken).ConfigureAwait(false); } /// @@ -141,7 +146,7 @@ protected AbstractFindUsagesService(IThreadingContext threadingContext) /// and want to push all the references to it into the Streaming-Find-References window. /// public static async Task FindSymbolReferencesAsync( - IThreadingContext threadingContext, + IThreadingContext threadingContext, ICodeIndexProvider? codeIndexProvider, IFindUsagesContext context, ISymbol symbol, Project project, CancellationToken cancellationToken) { await context.SetSearchTitleAsync(string.Format(EditorFeaturesResources._0_references, @@ -154,14 +159,53 @@ protected AbstractFindUsagesService(IThreadingContext threadingContext) // engine will push results into the 'progress' instance passed into it. // We'll take those results, massage them, and forward them along to the // FindReferencesContext instance we were given. - await SymbolFinder.FindReferencesAsync( + var normalFindReferencesTask = SymbolFinder.FindReferencesAsync( SymbolAndProjectId.Create(symbol, project.Id), project.Solution, progressAdapter, documents: null, - options: options, - cancellationToken: cancellationToken).ConfigureAwait(false); - ICodeIndexProvider + options, + cancellationToken); + + // Kick off work to search the online code index system in parallel + var codeIndexReferencesTask = FindCodeIndexReferencesAsync( + codeIndexProvider, + symbol, + project.Solution, + progressAdapter, + options, + cancellationToken); + + await Task.WhenAll(normalFindReferencesTask, codeIndexReferencesTask).ConfigureAwait(false); + } + + private async Task FindCodeIndexReferencesAsync( + ICodeIndexProvider? codeIndexProvider, + ISymbol symbol, Solution solution, + FindReferencesProgressAdapter progressAdapter, + FindReferencesSearchOptions options, + CancellationToken cancellationToken) + { + if (codeIndexProvider == null) + return; + + var moniker = SymbolMoniker.TryCreate(symbol); + if (moniker == null) + return; + + var monikers = SpecializedCollections.SingletonEnumerable(moniker); + var currentPage = 0; + while (true) + { + var results = await codeIndexProvider.FindReferencesByMonikerAsync( + monikers, includeDecleration: true, pageIndex: currentPage, cancellationToken: cancellationToken).ConfigureAwait(false); + if (results == null || results.Count == 0) + break; + + currentPage++; + await ProcessCodeIndexResultsAsync( + codeIndexProvider, results, cancellationToken).ConfigureAwait(false); + } } private async Task TryFindLiteralReferencesAsync( @@ -170,8 +214,8 @@ protected AbstractFindUsagesService(IThreadingContext threadingContext) var cancellationToken = context.CancellationToken; cancellationToken.ThrowIfCancellationRequested(); - var syntaxTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); - var syntaxFacts = document.GetLanguageService(); + var syntaxTree = await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); + var syntaxFacts = document.GetRequiredLanguageService(); // Currently we only support FAR for numbers, strings and characters. We don't // bother with true/false/null as those are likely to have way too many results @@ -192,20 +236,19 @@ protected AbstractFindUsagesService(IThreadingContext threadingContext) // for numeric values, and a decimal won't fit within that. var tokenValue = token.Value; if (tokenValue == null || tokenValue is decimal) - { return false; - } - var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + if (token.Parent is null) + return false; + + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); var symbol = semanticModel.GetSymbolInfo(token.Parent).Symbol ?? semanticModel.GetDeclaredSymbol(token.Parent); // Numeric labels are available in VB. In that case we want the normal FAR engine to // do the searching. For these literals we want to find symbolic results and not // numeric matches. if (symbol is ILabelSymbol) - { return false; - } // Use the literal to make the title. Trim literal if it's too long. var title = syntaxFacts.ConvertToSingleLine(token.Parent).ToString();