Esempio n. 1
0
        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));
                }
        }
Esempio n. 2
0
        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());
        }