From 68d61a2df833159c99badd66d8cb507ee67a5c5b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 22 Jul 2018 19:59:14 -0700 Subject: [PATCH] Take visible docs into account as well. --- .../NavigateToItemProvider.Searcher.cs | 97 ++++++++++++++----- .../NavigateTo/NavigateToItemProvider.cs | 2 +- ...stractNavigateToSearchService.InProcess.cs | 32 +++--- .../AbstractNavigateToSearchService.Remote.cs | 4 +- .../AbstractNavigateToSearchService.cs | 6 +- .../NavigateTo/INavigateToSearchService.cs | 2 +- .../IRemoteNavigateToSearchService.cs | 2 +- .../CodeAnalysisService_NavigateTo.cs | 8 +- 8 files changed, 103 insertions(+), 50 deletions(-) diff --git a/src/EditorFeatures/Core.Wpf/NavigateTo/NavigateToItemProvider.Searcher.cs b/src/EditorFeatures/Core.Wpf/NavigateTo/NavigateToItemProvider.Searcher.cs index 2c4dc9f11f4..ce55b55a740 100644 --- a/src/EditorFeatures/Core.Wpf/NavigateTo/NavigateToItemProvider.Searcher.cs +++ b/src/EditorFeatures/Core.Wpf/NavigateTo/NavigateToItemProvider.Searcher.cs @@ -1,12 +1,14 @@ // 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; +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.NavigateTo; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text.Shared.Extensions; @@ -70,30 +72,13 @@ internal async void Search() var workspace = _solution.Workspace; var docTrackingService = workspace.Services.GetService(); - var activeDocIdOpt = docTrackingService?.GetActiveDocument(); - var activeDocumentOpt = _solution.GetDocument(activeDocIdOpt); - - if (activeDocumentOpt != null) + if (docTrackingService != null) { - var activeProject = activeDocumentOpt.Project; - - // Search the current project first. That way we can deliver - // results that are closer in scope to the user quicker without - // forcing them to do something like NavToInCurrentDoc - await Task.Run(() => SearchAsync(activeProject, activeDocumentOpt), _cancellationToken).ConfigureAwait(false); - var searchTasks = _solution.Projects - .Where(p => p != activeProject) - .Select(p => Task.Run(() => SearchAsync(p, activeDocumentOpt: null), _cancellationToken)).ToArray(); - - await Task.WhenAll(searchTasks).ConfigureAwait(false); + await SearchProjectsInPriorityOrder(docTrackingService).ConfigureAwait(false); } else { - // Search each project with an independent threadpool task. - var searchTasks = _solution.Projects.Select( - p => Task.Run(() => SearchAsync(p, activeDocumentOpt: null), _cancellationToken)).ToArray(); - - await Task.WhenAll(searchTasks).ConfigureAwait(false); + await SearchAllProjectsAsync().ConfigureAwait(false); } } } @@ -106,11 +91,75 @@ internal async void Search() } } - private async Task SearchAsync(Project project, Document activeDocumentOpt) + private async Task SearchProjectsInPriorityOrder(IDocumentTrackingService docTrackingService) + { + var processedProjects = new HashSet(); + + var activeDocOpt = _solution.GetDocument(docTrackingService.GetActiveDocument()); + var visibleDocs = docTrackingService.GetVisibleDocuments() + .Select(d => _solution.GetDocument(d)) + .Where(d => d != activeDocOpt) + .WhereNotNull() + .ToImmutableArray(); + + // First, if there's an active document, search that project first, prioritizing + // that active document and all visible documents from it. + if (activeDocOpt != null) + { + var activeProject = activeDocOpt.Project; + processedProjects.Add(activeProject); + + var visibleDocsFromProject = visibleDocs.Where(d => d.Project == activeProject); + var priorityDocs = ImmutableArray.Create(activeDocOpt).AddRange(visibleDocsFromProject); + + // Search the current project first. That way we can deliver + // results that are closer in scope to the user quicker without + // forcing them to do something like NavToInCurrentDoc + await Task.Run(() => SearchAsync(activeProject, priorityDocs), _cancellationToken).ConfigureAwait(false); + } + + // Now, process all visible docs that were not processed in the project for the + // active doc. + var tasks = new List(); + foreach (var group in visibleDocs.GroupBy(d => d.Project)) + { + var currentProject = group.Key; + + // make sure we only process this project if we didn't already process it above. + if (processedProjects.Add(currentProject)) + { + tasks.Add(Task.Run(() => SearchAsync(currentProject, group.ToImmutableArray()), _cancellationToken)); + } + } + + await Task.WhenAll(tasks).ConfigureAwait(false); + + // Now, process the remainder of projects + tasks.Clear(); + foreach (var currentProject in _solution.Projects) + { + // make sure we only process this project if we didn't already process it above. + if (processedProjects.Add(currentProject)) + { + tasks.Add(Task.Run(() => SearchAsync(currentProject, ImmutableArray.Empty), _cancellationToken)); + } + } + } + + private async Task SearchAllProjectsAsync() + { + // Search each project with an independent threadpool task. + var searchTasks = _solution.Projects.Select( + p => Task.Run(() => SearchAsync(p, priorityDocuments: ImmutableArray.Empty), _cancellationToken)).ToArray(); + + await Task.WhenAll(searchTasks).ConfigureAwait(false); + } + + private async Task SearchAsync(Project project, ImmutableArray priorityDocuments) { try { - await SearchAsyncWorker(project, activeDocumentOpt).ConfigureAwait(false); + await SearchAsyncWorker(project, priorityDocuments).ConfigureAwait(false); } finally { @@ -118,7 +167,7 @@ private async Task SearchAsync(Project project, Document activeDocumentOpt) } } - private async Task SearchAsyncWorker(Project project, Document activeDocumentOpt) + private async Task SearchAsyncWorker(Project project, ImmutableArray priorityDocuments) { if (_searchCurrentDocument && _currentDocument?.Project != project) { @@ -135,7 +184,7 @@ private async Task SearchAsyncWorker(Project project, Document activeDocumentOpt { var searchTask = _currentDocument != null ? service.SearchDocumentAsync(_currentDocument, _searchPattern, _kinds, _cancellationToken) - : service.SearchProjectAsync(project, activeDocumentOpt, _searchPattern, _kinds, _cancellationToken); + : service.SearchProjectAsync(project, priorityDocuments, _searchPattern, _kinds, _cancellationToken); var results = await searchTask.ConfigureAwait(false); if (results != null) diff --git a/src/EditorFeatures/Core.Wpf/NavigateTo/NavigateToItemProvider.cs b/src/EditorFeatures/Core.Wpf/NavigateTo/NavigateToItemProvider.cs index 480c09f7b14..be9b5cc4884 100644 --- a/src/EditorFeatures/Core.Wpf/NavigateTo/NavigateToItemProvider.cs +++ b/src/EditorFeatures/Core.Wpf/NavigateTo/NavigateToItemProvider.cs @@ -190,7 +190,7 @@ public ShimNavigateToSearchService(INavigateToSearchService navigateToSearchServ public Task> SearchDocumentAsync(Document document, string searchPattern, IImmutableSet kinds, CancellationToken cancellationToken) => _navigateToSearchService.SearchDocumentAsync(document, searchPattern, cancellationToken); - public Task> SearchProjectAsync(Project project, string searchPattern, IImmutableSet kinds, CancellationToken cancellationToken) + public Task> SearchProjectAsync(Project project, ImmutableArray priorityDocuments, string searchPattern, IImmutableSet kinds, CancellationToken cancellationToken) => _navigateToSearchService.SearchProjectAsync(project, searchPattern, cancellationToken); } } diff --git a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.InProcess.cs b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.InProcess.cs index 62774f5067c..04a08345e53 100644 --- a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.InProcess.cs +++ b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.InProcess.cs @@ -24,21 +24,22 @@ internal abstract partial class AbstractNavigateToSearchService new ConditionalWeakTable>>(); public static Task> SearchProjectInCurrentProcessAsync( - Project project, Document activeDocumentOpt, string searchPattern, IImmutableSet kinds, CancellationToken cancellationToken) + Project project, ImmutableArray priorityDocuments, string searchPattern, IImmutableSet kinds, CancellationToken cancellationToken) { return FindSearchResultsAsync( - project, activeDocumentOpt, searchDocument: null, pattern: searchPattern, kinds, cancellationToken: cancellationToken); + project, priorityDocuments, searchDocument: null, pattern: searchPattern, kinds, cancellationToken: cancellationToken); } public static Task> SearchDocumentInCurrentProcessAsync( Document document, string searchPattern, IImmutableSet kinds, CancellationToken cancellationToken) { return FindSearchResultsAsync( - document.Project, activeDocumentOpt: null, document, searchPattern, kinds, cancellationToken); + document.Project, priorityDocuments: ImmutableArray.Empty, + document, searchPattern, kinds, cancellationToken); } private static async Task> FindSearchResultsAsync( - Project project, Document activeDocumentOpt, Document searchDocument, + Project project, ImmutableArray priorityDocuments, Document searchDocument, string pattern, IImmutableSet kinds, CancellationToken cancellationToken) { // If the user created a dotted pattern then we'll grab the last part of the name @@ -68,8 +69,8 @@ internal abstract partial class AbstractNavigateToSearchService // scratch. #if true var task = searchDocument != null - ? ComputeSearchResultsAsync(project, activeDocumentOpt, searchDocument, nameMatcher, containerMatcherOpt, declaredSymbolInfoKindsSet, nameMatches, containerMatches, cancellationToken) - : TryFilterPreviousSearchResultsAsync(project, activeDocumentOpt, searchDocument, pattern, nameMatcher, containerMatcherOpt, declaredSymbolInfoKindsSet, nameMatches, containerMatches, cancellationToken); + ? ComputeSearchResultsAsync(project, priorityDocuments, searchDocument, nameMatcher, containerMatcherOpt, declaredSymbolInfoKindsSet, nameMatches, containerMatches, cancellationToken) + : TryFilterPreviousSearchResultsAsync(project, priorityDocuments, searchDocument, pattern, nameMatcher, containerMatcherOpt, declaredSymbolInfoKindsSet, nameMatches, containerMatches, cancellationToken); #else var task = ComputeSearchResultsAsync(project, searchDocument, nameMatcher, containerMatcherOpt, declaredSymbolInfoKindsSet, nameMatches, containerMatches, cancellationToken); #endif @@ -86,7 +87,8 @@ internal abstract partial class AbstractNavigateToSearchService } private static async Task> TryFilterPreviousSearchResultsAsync( - Project project, Document activeDocumentOpt, Document searchDocument, string pattern, + Project project, ImmutableArray priorityDocuments, + Document searchDocument, string pattern, PatternMatcher nameMatcher, PatternMatcher containerMatcherOpt, DeclaredSymbolInfoKindSet kinds, ArrayBuilder nameMatches, ArrayBuilder containerMatches, @@ -113,7 +115,7 @@ internal abstract partial class AbstractNavigateToSearchService // Didn't have previous results. Or it was a very different pattern. // Can't reuse. searchResults = await ComputeSearchResultsAsync( - project, activeDocumentOpt, searchDocument, + project, priorityDocuments, searchDocument, nameMatcher, containerMatcherOpt, kinds, nameMatches, containerMatches, cancellationToken).ConfigureAwait(false); } @@ -153,7 +155,7 @@ internal abstract partial class AbstractNavigateToSearchService } private static async Task> ComputeSearchResultsAsync( - Project project, Document activeDocumentOpt, Document searchDocument, + Project project, ImmutableArray priorityDocuments, Document searchDocument, PatternMatcher nameMatcher, PatternMatcher containerMatcherOpt, DeclaredSymbolInfoKindSet kinds, ArrayBuilder nameMatches, ArrayBuilder containerMatches, @@ -161,13 +163,13 @@ internal abstract partial class AbstractNavigateToSearchService { var result = ArrayBuilder.GetInstance(); - // Prioritize the active document if we have one. - var documents = activeDocumentOpt == null - ? project.Documents - : project.Documents.Where(d => d == activeDocumentOpt) - .Concat(project.Documents.Where(d => d != activeDocumentOpt)); + // Prioritize the active documents if we have any. + var prioritySet = priorityDocuments.ToSet(); - foreach (var document in documents) + var highPriDocs = project.Documents.Where(d => prioritySet.Contains(d)); + var lowPriDocs = project.Documents.Where(d => !prioritySet.Contains(d)); + + foreach (var document in highPriDocs.Concat(lowPriDocs)) { if (searchDocument != null && document != searchDocument) { diff --git a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.Remote.cs b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.Remote.cs index abe28854c15..c37fbe5a08f 100644 --- a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.Remote.cs +++ b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.Remote.cs @@ -25,13 +25,13 @@ internal abstract partial class AbstractNavigateToSearchService } private async Task> SearchProjectInRemoteProcessAsync( - RemoteHostClient client, Project project, Document activeDocumentOpt, string searchPattern, IImmutableSet kinds, CancellationToken cancellationToken) + RemoteHostClient client, Project project, ImmutableArray priorityDocuments, string searchPattern, IImmutableSet kinds, CancellationToken cancellationToken) { var solution = project.Solution; var serializableResults = await client.TryRunCodeAnalysisRemoteAsync>( solution, nameof(IRemoteNavigateToSearchService.SearchProjectAsync), - new object[] { project.Id, activeDocumentOpt?.Id, searchPattern, kinds.ToArray() }, cancellationToken).ConfigureAwait(false); + new object[] { project.Id, priorityDocuments.Select(d => d.Id).ToArray(), searchPattern, kinds.ToArray() }, cancellationToken).ConfigureAwait(false); return serializableResults.SelectAsArray(r => r.Rehydrate(solution)); } diff --git a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.cs b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.cs index eb5edc7f94f..fb5f96e2e8a 100644 --- a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.cs +++ b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.cs @@ -42,18 +42,18 @@ internal abstract partial class AbstractNavigateToSearchService : INavigateToSea } public async Task> SearchProjectAsync( - Project project, Document activeDocumentOpt, string searchPattern, IImmutableSet kinds, CancellationToken cancellationToken) + Project project, ImmutableArray priorityDocuments, string searchPattern, IImmutableSet kinds, CancellationToken cancellationToken) { var client = await TryGetRemoteHostClientAsync(project, cancellationToken).ConfigureAwait(false); if (client == null) { return await SearchProjectInCurrentProcessAsync( - project, activeDocumentOpt, searchPattern, kinds, cancellationToken).ConfigureAwait(false); + project, priorityDocuments, searchPattern, kinds, cancellationToken).ConfigureAwait(false); } else { return await SearchProjectInRemoteProcessAsync( - client, activeDocumentOpt, project, searchPattern, kinds, cancellationToken).ConfigureAwait(false); + client, project, priorityDocuments, searchPattern, kinds, cancellationToken).ConfigureAwait(false); } } } diff --git a/src/Features/Core/Portable/NavigateTo/INavigateToSearchService.cs b/src/Features/Core/Portable/NavigateTo/INavigateToSearchService.cs index e840e211e98..516a5409407 100644 --- a/src/Features/Core/Portable/NavigateTo/INavigateToSearchService.cs +++ b/src/Features/Core/Portable/NavigateTo/INavigateToSearchService.cs @@ -28,7 +28,7 @@ bool CanFilter get; } - Task> SearchProjectAsync(Project project, Document activeDocumentOpt, string searchPattern, IImmutableSet kinds, CancellationToken cancellationToken); + Task> SearchProjectAsync(Project project, ImmutableArray priorityDocuments, string searchPattern, IImmutableSet kinds, CancellationToken cancellationToken); Task> SearchDocumentAsync(Document document, string searchPattern, IImmutableSet kinds, CancellationToken cancellationToken); } } diff --git a/src/Features/Core/Portable/NavigateTo/IRemoteNavigateToSearchService.cs b/src/Features/Core/Portable/NavigateTo/IRemoteNavigateToSearchService.cs index 1a04b552ba2..5a3e0cdcd01 100644 --- a/src/Features/Core/Portable/NavigateTo/IRemoteNavigateToSearchService.cs +++ b/src/Features/Core/Portable/NavigateTo/IRemoteNavigateToSearchService.cs @@ -10,6 +10,6 @@ namespace Microsoft.CodeAnalysis.NavigateTo internal interface IRemoteNavigateToSearchService { Task> SearchDocumentAsync(DocumentId documentId, string searchPattern, string[] kinds, CancellationToken cancellationToken); - Task> SearchProjectAsync(ProjectId projectId, DocumentId activeDocumentIdOpt, string searchPattern, string[] kinds, CancellationToken cancellationToken); + Task> SearchProjectAsync(ProjectId projectId, DocumentId[] priorityDocumentIds, string searchPattern, string[] kinds, CancellationToken cancellationToken); } } diff --git a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_NavigateTo.cs b/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_NavigateTo.cs index 3048cbe556f..01512bffea8 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_NavigateTo.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_NavigateTo.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Collections.Immutable; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.NavigateTo; @@ -29,7 +30,7 @@ internal partial class CodeAnalysisService : IRemoteNavigateToSearchService } public Task> SearchProjectAsync( - ProjectId projectId, DocumentId activeDocIdOpt, string searchPattern, string[] kinds, CancellationToken cancellationToken) + ProjectId projectId, DocumentId[] priorityDocumentIds, string searchPattern, string[] kinds, CancellationToken cancellationToken) { return RunServiceAsync(async token => { @@ -38,10 +39,11 @@ internal partial class CodeAnalysisService : IRemoteNavigateToSearchService var solution = await GetSolutionAsync(token).ConfigureAwait(false); var project = solution.GetProject(projectId); - var activeDocumentOpt = solution.GetDocument(activeDocIdOpt); + var priorityDocuments = priorityDocumentIds.Select(d => solution.GetDocument(d)) + .ToImmutableArray(); var result = await AbstractNavigateToSearchService.SearchProjectInCurrentProcessAsync( - project, activeDocumentOpt, searchPattern, kinds.ToImmutableHashSet(), token).ConfigureAwait(false); + project, priorityDocuments, searchPattern, kinds.ToImmutableHashSet(), token).ConfigureAwait(false); return Convert(result); } -- GitLab