public async Task SearchAsync( bool searchCurrentDocument, NavigateToSearchScope scope, CancellationToken cancellationToken) { var isFullyLoaded = true; try { using var navigateToSearch = Logger.LogBlock(FunctionId.NavigateTo_Search, KeyValueLogMessage.Create(LogType.UserAction), cancellationToken); if (searchCurrentDocument) { await SearchCurrentDocumentAsync(cancellationToken).ConfigureAwait(false); } else { // We consider ourselves fully loaded when both the project system has completed loaded us, and we've // totally hydrated the oop side. Until that happens, we'll attempt to return cached data from languages // that support that. isFullyLoaded = await _host.IsFullyLoadedAsync(cancellationToken).ConfigureAwait(false); await SearchAllProjectsAsync(isFullyLoaded, scope, cancellationToken).ConfigureAwait(false); } } finally { // Ensure that we actually complete all our remaining progress items so that the progress bar completes. await ProgressItemsCompletedAsync(_remainingProgressItems, cancellationToken).ConfigureAwait(false); Debug.Assert(_remainingProgressItems == 0); // Pass along isFullyLoaded so that the UI can show indication to users that results may be incomplete. _callback.Done(isFullyLoaded); } }
public SearchGraphQuery( string searchPattern, NavigateToSearchScope searchScope, IThreadingContext threadingContext, IAsynchronousOperationListener asyncListener) { _threadingContext = threadingContext; _asyncListener = asyncListener; _searchPattern = searchPattern; _searchScope = searchScope; }
private async Task SearchAllProjectsAsync( bool isFullyLoaded, NavigateToSearchScope scope, CancellationToken cancellationToken) { var seenItems = new HashSet <INavigateToSearchResult>(NavigateToSearchResultComparer.Instance); var orderedProjects = GetOrderedProjectsToProcess(); var searchRegularDocuments = scope.HasFlag(NavigateToSearchScope.RegularDocuments); var searchGeneratedDocuments = scope.HasFlag(NavigateToSearchScope.GeneratedDocuments); Debug.Assert(searchRegularDocuments || searchGeneratedDocuments); var projectCount = orderedProjects.Sum(g => g.Length); if (isFullyLoaded) { // We may do up to two passes. One for loaded docs. One for source generated docs. await AddProgressItemsAsync( projectCount *((searchRegularDocuments ? 1 : 0) + (searchGeneratedDocuments ? 1 : 0)), cancellationToken).ConfigureAwait(false); if (searchRegularDocuments) { await SearchFullyLoadedProjectsAsync(orderedProjects, seenItems, cancellationToken).ConfigureAwait(false); } if (searchGeneratedDocuments) { await SearchGeneratedDocumentsAsync(seenItems, cancellationToken).ConfigureAwait(false); } } else { // If we're not fully loaded, we only search regular documents. Generated documents must wait until // we're fully loaded (and thus have all the information necessary to properly run generators). if (searchRegularDocuments) { // We do at least two passes. One for cached docs. One for normal docs. await AddProgressItemsAsync( projectCount * 2, cancellationToken).ConfigureAwait(false); await SearchCachedDocumentsAsync(orderedProjects, seenItems, cancellationToken).ConfigureAwait(false); // If searching cached data returned any results, then we're done. We've at least shown some results // to the user. That will hopefully serve them well enough until the solution fully loads. if (seenItems.Count > 0) { return; } await SearchFullyLoadedProjectsAsync(orderedProjects, seenItems, cancellationToken).ConfigureAwait(false); // Report a telemetry event to track if we found uncached items after failing to find cached items. // In practice if we see that we are always finding uncached items, then it's likely something // has broken in the caching system since we would expect to normally find values there. Specifically // we expect: foundFullItems <<< not foundFullItems. Logger.Log(FunctionId.NavigateTo_CacheItemsMiss, KeyValueLogMessage.Create(m => m["FoundFullItems"] = seenItems.Count > 0)); } } }