private static async Task <ImmutableArray <INavigateToSearchResult> > FindSearchResultsAsync( 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 var(patternName, patternContainerOpt) = PatternMatcher.GetNameAndContainer(pattern); var nameMatcher = PatternMatcher.CreatePatternMatcher(patternName, includeMatchedSpans: true, allowFuzzyMatching: true); var containerMatcherOpt = patternContainerOpt != null ? PatternMatcher.CreateDotSeparatedContainerMatcher(patternContainerOpt) : null; using (nameMatcher) using (containerMatcherOpt) { using var _1 = ArrayBuilder <PatternMatch> .GetInstance(out var nameMatches); using var _2 = ArrayBuilder <PatternMatch> .GetInstance(out var containerMatches); var declaredSymbolInfoKindsSet = new DeclaredSymbolInfoKindSet(kinds); var searchResults = await ComputeSearchResultsAsync( project, priorityDocuments, searchDocument, nameMatcher, containerMatcherOpt, declaredSymbolInfoKindsSet, nameMatches, containerMatches, cancellationToken).ConfigureAwait(false); return(ImmutableArray <INavigateToSearchResult> .CastUp(searchResults)); } }
private static async Task <ImmutableArray <SearchResult> > ComputeSearchResultsAsync( Project project, Document searchDocument, PatternMatcher nameMatcher, PatternMatcher containerMatcherOpt, DeclaredSymbolInfoKindSet kinds, ArrayBuilder <PatternMatch> nameMatches, ArrayBuilder <PatternMatch> containerMatches, CancellationToken cancellationToken) { var result = ArrayBuilder <SearchResult> .GetInstance(); foreach (var document in project.Documents) { if (searchDocument != null && document != searchDocument) { continue; } cancellationToken.ThrowIfCancellationRequested(); var declarationInfo = await document.GetSyntaxTreeIndexAsync(cancellationToken).ConfigureAwait(false); foreach (var declaredSymbolInfo in declarationInfo.DeclaredSymbolInfos) { AddResultIfMatch( document, declaredSymbolInfo, nameMatcher, containerMatcherOpt, kinds, nameMatches, containerMatches, result, cancellationToken); } } return(result.ToImmutableAndFree()); }
private static async Task <ImmutableArray <INavigateToSearchResult> > FindSearchResultsAsync( 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 var(patternName, patternContainerOpt) = PatternMatcher.GetNameAndContainer(pattern); var nameMatcher = PatternMatcher.CreatePatternMatcher(patternName, includeMatchedSpans: true, allowFuzzyMatching: true); var containerMatcherOpt = patternContainerOpt != null ? PatternMatcher.CreateDotSeparatedContainerMatcher(patternContainerOpt) : null; using (nameMatcher) using (containerMatcherOpt) { var nameMatches = ArrayBuilder <PatternMatch> .GetInstance(); var containerMatches = ArrayBuilder <PatternMatch> .GetInstance(); try { var declaredSymbolInfoKindsSet = new DeclaredSymbolInfoKindSet(kinds); // If we're searching a single document, then just do a full search of // that document (we're fast enough to not need to optimize that case). // // If, however, we are searching a project, then see if we could potentially // use the last computed results we have for that project. If so, it can // be much faster to reuse and filter that result than to compute it from // scratch. #if true var task = searchDocument != null ? 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 var searchResults = await task.ConfigureAwait(false); return(ImmutableArray <INavigateToSearchResult> .CastUp(searchResults)); } finally { nameMatches.Free(); containerMatches.Free(); } } }
private static async Task <ImmutableArray <SearchResult> > TryFilterPreviousSearchResultsAsync( Project project, ImmutableArray <Document> priorityDocuments, Document searchDocument, string pattern, PatternMatcher nameMatcher, PatternMatcher containerMatcherOpt, DeclaredSymbolInfoKindSet kinds, ArrayBuilder <PatternMatch> nameMatches, ArrayBuilder <PatternMatch> containerMatches, CancellationToken cancellationToken) { // Searching an entire project. See if we already performed that same // search with a substring of the current pattern. if so, we can use // the previous result and just filter that down. This is useful for // the common case where a user types some pattern, then keeps adding // to it. ImmutableArray <SearchResult> searchResults; if (s_lastProjectSearchCache.TryGetValue(project, out var previousResult) && pattern.StartsWith(previousResult.Item1)) { // We can reuse the previous results and just filter them. searchResults = FilterPreviousResults( previousResult.Item2, nameMatcher, containerMatcherOpt, kinds, nameMatches, containerMatches, cancellationToken); } else { // Didn't have previous results. Or it was a very different pattern. // Can't reuse. searchResults = await ComputeSearchResultsAsync( project, priorityDocuments, searchDocument, nameMatcher, containerMatcherOpt, kinds, nameMatches, containerMatches, cancellationToken).ConfigureAwait(false); } // Would like to use CWT.AddOrUpdate. But that is not available on the // version of .NET that we're using. So we need to take lock as we're // making multiple mutations. lock (s_lastProjectSearchCache) { s_lastProjectSearchCache.Remove(project); s_lastProjectSearchCache.Add(project, Tuple.Create(pattern, searchResults)); } return(searchResults); }
private static async Task <ImmutableArray <SearchResult> > ComputeSearchResultsAsync( Project project, ImmutableArray <Document> priorityDocuments, Document searchDocument, PatternMatcher nameMatcher, PatternMatcher containerMatcherOpt, DeclaredSymbolInfoKindSet kinds, ArrayBuilder <PatternMatch> nameMatches, ArrayBuilder <PatternMatch> containerMatches, CancellationToken cancellationToken) { var result = ArrayBuilder <SearchResult> .GetInstance(); // Prioritize the active documents if we have any. var highPriDocs = priorityDocuments.Where(d => project.ContainsDocument(d.Id)) .ToImmutableArray(); var highPriDocsSet = highPriDocs.ToSet(); var lowPriDocs = project.Documents.Where(d => !highPriDocsSet.Contains(d)); var orderedDocs = highPriDocs.AddRange(lowPriDocs); Debug.Assert(priorityDocuments.All(d => project.ContainsDocument(d.Id)), "Priority docs included doc not from project."); Debug.Assert(orderedDocs.Length == project.Documents.Count(), "Didn't have the same number of project after ordering them!"); Debug.Assert(orderedDocs.Distinct().Length == orderedDocs.Length, "Ordered list contained a duplicate!"); Debug.Assert(project.Documents.All(d => orderedDocs.Contains(d)), "At least one document from the project was missing from the ordered list!"); foreach (var document in orderedDocs) { if (searchDocument != null && document != searchDocument) { continue; } cancellationToken.ThrowIfCancellationRequested(); var declarationInfo = await document.GetSyntaxTreeIndexAsync(cancellationToken).ConfigureAwait(false); foreach (var declaredSymbolInfo in declarationInfo.DeclaredSymbolInfos) { AddResultIfMatch( document, declaredSymbolInfo, nameMatcher, containerMatcherOpt, kinds, nameMatches, containerMatches, result, cancellationToken); } } return(result.ToImmutableAndFree()); }
public static async Task SearchGeneratedDocumentsInCurrentProcessAsync( Project project, string pattern, IImmutableSet <string> kinds, Func <RoslynNavigateToItem, Task> onResultFound, CancellationToken cancellationToken) { // If the user created a dotted pattern then we'll grab the last part of the name var(patternName, patternContainerOpt) = PatternMatcher.GetNameAndContainer(pattern); var declaredSymbolInfoKindsSet = new DeclaredSymbolInfoKindSet(kinds); // First generate all the source-gen docs. Then handoff to the standard search routine to find matches in them. var generatedDocs = await project.GetSourceGeneratedDocumentsAsync(cancellationToken).ConfigureAwait(false); await ProcessDocumentsAsync(searchDocument : null, patternName, patternContainerOpt, declaredSymbolInfoKindsSet, onResultFound, generatedDocs.ToSet <Document>(), cancellationToken).ConfigureAwait(false); }
private static void AddResultIfMatch( Document document, DeclaredSymbolInfo declaredSymbolInfo, PatternMatcher nameMatcher, PatternMatcher containerMatcherOpt, DeclaredSymbolInfoKindSet kinds, ArrayBuilder <PatternMatch> nameMatches, ArrayBuilder <PatternMatch> containerMatches, ArrayBuilder <SearchResult> result, CancellationToken cancellationToken) { nameMatches.Clear(); containerMatches.Clear(); cancellationToken.ThrowIfCancellationRequested(); if (kinds.Contains(declaredSymbolInfo.Kind) && nameMatcher.AddMatches(declaredSymbolInfo.Name, nameMatches) && containerMatcherOpt?.AddMatches(declaredSymbolInfo.FullyQualifiedContainerName, containerMatches) != false) { result.Add(ConvertResult( declaredSymbolInfo, document, nameMatches, containerMatches)); } }
private static ImmutableArray <SearchResult> FilterPreviousResults( ImmutableArray <SearchResult> previousResults, PatternMatcher nameMatcher, PatternMatcher containerMatcherOpt, DeclaredSymbolInfoKindSet kinds, ArrayBuilder <PatternMatch> nameMatches, ArrayBuilder <PatternMatch> containerMatches, CancellationToken cancellationToken) { var result = ArrayBuilder <SearchResult> .GetInstance(); foreach (var previousResult in previousResults) { var document = previousResult.Document; var info = previousResult.DeclaredSymbolInfo; AddResultIfMatch( document, info, nameMatcher, containerMatcherOpt, kinds, nameMatches, containerMatches, result, cancellationToken); } return(result.ToImmutableAndFree()); }