提交 68d61a2d 编写于 作者: C Cyrus Najmabadi

Take visible docs into account as well.

上级 62d25585
// 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<IDocumentTrackingService>();
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<Project>();
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<Task>();
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<Document>.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<Document>.Empty), _cancellationToken)).ToArray();
await Task.WhenAll(searchTasks).ConfigureAwait(false);
}
private async Task SearchAsync(Project project, ImmutableArray<Document> 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<Document> 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)
......
......@@ -190,7 +190,7 @@ public ShimNavigateToSearchService(INavigateToSearchService navigateToSearchServ
public Task<ImmutableArray<INavigateToSearchResult>> SearchDocumentAsync(Document document, string searchPattern, IImmutableSet<string> kinds, CancellationToken cancellationToken)
=> _navigateToSearchService.SearchDocumentAsync(document, searchPattern, cancellationToken);
public Task<ImmutableArray<INavigateToSearchResult>> SearchProjectAsync(Project project, string searchPattern, IImmutableSet<string> kinds, CancellationToken cancellationToken)
public Task<ImmutableArray<INavigateToSearchResult>> SearchProjectAsync(Project project, ImmutableArray<Document> priorityDocuments, string searchPattern, IImmutableSet<string> kinds, CancellationToken cancellationToken)
=> _navigateToSearchService.SearchProjectAsync(project, searchPattern, cancellationToken);
}
}
......
......@@ -24,21 +24,22 @@ internal abstract partial class AbstractNavigateToSearchService
new ConditionalWeakTable<Project, Tuple<string, ImmutableArray<SearchResult>>>();
public static Task<ImmutableArray<INavigateToSearchResult>> SearchProjectInCurrentProcessAsync(
Project project, Document activeDocumentOpt, string searchPattern, IImmutableSet<string> kinds, CancellationToken cancellationToken)
Project project, ImmutableArray<Document> priorityDocuments, string searchPattern, IImmutableSet<string> 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<ImmutableArray<INavigateToSearchResult>> SearchDocumentInCurrentProcessAsync(
Document document, string searchPattern, IImmutableSet<string> kinds, CancellationToken cancellationToken)
{
return FindSearchResultsAsync(
document.Project, activeDocumentOpt: null, document, searchPattern, kinds, cancellationToken);
document.Project, priorityDocuments: ImmutableArray<Document>.Empty,
document, searchPattern, kinds, cancellationToken);
}
private static async Task<ImmutableArray<INavigateToSearchResult>> FindSearchResultsAsync(
Project project, Document activeDocumentOpt, Document searchDocument,
Project project, ImmutableArray<Document> priorityDocuments, Document searchDocument,
string pattern, IImmutableSet<string> 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<ImmutableArray<SearchResult>> TryFilterPreviousSearchResultsAsync(
Project project, Document activeDocumentOpt, Document searchDocument, string pattern,
Project project, ImmutableArray<Document> priorityDocuments,
Document searchDocument, string pattern,
PatternMatcher nameMatcher, PatternMatcher containerMatcherOpt,
DeclaredSymbolInfoKindSet kinds,
ArrayBuilder<PatternMatch> nameMatches, ArrayBuilder<PatternMatch> 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<ImmutableArray<SearchResult>> ComputeSearchResultsAsync(
Project project, Document activeDocumentOpt, Document searchDocument,
Project project, ImmutableArray<Document> priorityDocuments, Document searchDocument,
PatternMatcher nameMatcher, PatternMatcher containerMatcherOpt,
DeclaredSymbolInfoKindSet kinds,
ArrayBuilder<PatternMatch> nameMatches, ArrayBuilder<PatternMatch> containerMatches,
......@@ -161,13 +163,13 @@ internal abstract partial class AbstractNavigateToSearchService
{
var result = ArrayBuilder<SearchResult>.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)
{
......
......@@ -25,13 +25,13 @@ internal abstract partial class AbstractNavigateToSearchService
}
private async Task<ImmutableArray<INavigateToSearchResult>> SearchProjectInRemoteProcessAsync(
RemoteHostClient client, Project project, Document activeDocumentOpt, string searchPattern, IImmutableSet<string> kinds, CancellationToken cancellationToken)
RemoteHostClient client, Project project, ImmutableArray<Document> priorityDocuments, string searchPattern, IImmutableSet<string> kinds, CancellationToken cancellationToken)
{
var solution = project.Solution;
var serializableResults = await client.TryRunCodeAnalysisRemoteAsync<IList<SerializableNavigateToSearchResult>>(
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));
}
......
......@@ -42,18 +42,18 @@ internal abstract partial class AbstractNavigateToSearchService : INavigateToSea
}
public async Task<ImmutableArray<INavigateToSearchResult>> SearchProjectAsync(
Project project, Document activeDocumentOpt, string searchPattern, IImmutableSet<string> kinds, CancellationToken cancellationToken)
Project project, ImmutableArray<Document> priorityDocuments, string searchPattern, IImmutableSet<string> 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);
}
}
}
......
......@@ -28,7 +28,7 @@ bool CanFilter
get;
}
Task<ImmutableArray<INavigateToSearchResult>> SearchProjectAsync(Project project, Document activeDocumentOpt, string searchPattern, IImmutableSet<string> kinds, CancellationToken cancellationToken);
Task<ImmutableArray<INavigateToSearchResult>> SearchProjectAsync(Project project, ImmutableArray<Document> priorityDocuments, string searchPattern, IImmutableSet<string> kinds, CancellationToken cancellationToken);
Task<ImmutableArray<INavigateToSearchResult>> SearchDocumentAsync(Document document, string searchPattern, IImmutableSet<string> kinds, CancellationToken cancellationToken);
}
}
......@@ -10,6 +10,6 @@ namespace Microsoft.CodeAnalysis.NavigateTo
internal interface IRemoteNavigateToSearchService
{
Task<IList<SerializableNavigateToSearchResult>> SearchDocumentAsync(DocumentId documentId, string searchPattern, string[] kinds, CancellationToken cancellationToken);
Task<IList<SerializableNavigateToSearchResult>> SearchProjectAsync(ProjectId projectId, DocumentId activeDocumentIdOpt, string searchPattern, string[] kinds, CancellationToken cancellationToken);
Task<IList<SerializableNavigateToSearchResult>> SearchProjectAsync(ProjectId projectId, DocumentId[] priorityDocumentIds, string searchPattern, string[] kinds, CancellationToken cancellationToken);
}
}
......@@ -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<IList<SerializableNavigateToSearchResult>> 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);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册