NavigateToItemProvider.Searcher.cs 8.1 KB
Newer Older
S
Sam Harwell 已提交
1
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.
2 3

using System;
4
using System.Collections.Immutable;
5 6 7 8
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Internal.Log;
9
using Microsoft.CodeAnalysis.NavigateTo;
10 11
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.CodeAnalysis.Shared.Utilities;
12
using Microsoft.CodeAnalysis.Text.Shared.Extensions;
13
using Microsoft.VisualStudio.Language.NavigateTo.Interfaces;
14 15
using Microsoft.VisualStudio.Text.PatternMatching;
using Roslyn.Utilities;
16 17 18 19 20 21 22 23

namespace Microsoft.CodeAnalysis.Editor.Implementation.NavigateTo
{
    internal partial class NavigateToItemProvider
    {
        private class Searcher
        {
            private readonly Solution _solution;
24
            private readonly INavigateToItemDisplayFactory _displayFactory;
25 26
            private readonly INavigateToCallback _callback;
            private readonly string _searchPattern;
27
            private readonly bool _searchCurrentDocument;
28
            private readonly IImmutableSet<string> _kinds;
29
            private readonly Document _currentDocument;
30 31 32 33 34 35 36
            private readonly ProgressTracker _progress;
            private readonly IAsynchronousOperationListener _asyncListener;
            private readonly CancellationToken _cancellationToken;

            public Searcher(
                Solution solution,
                IAsynchronousOperationListener asyncListener,
37
                INavigateToItemDisplayFactory displayFactory,
38 39
                INavigateToCallback callback,
                string searchPattern,
40
                bool searchCurrentDocument,
41
                IImmutableSet<string> kinds,
42 43 44 45 46 47
                CancellationToken cancellationToken)
            {
                _solution = solution;
                _displayFactory = displayFactory;
                _callback = callback;
                _searchPattern = searchPattern;
48
                _searchCurrentDocument = searchCurrentDocument;
49
                _kinds = kinds;
50 51 52
                _cancellationToken = cancellationToken;
                _progress = new ProgressTracker(callback.ReportProgress);
                _asyncListener = asyncListener;
53 54 55 56 57 58 59

                if (_searchCurrentDocument)
                {
                    var documentService = _solution.Workspace.Services.GetService<IDocumentTrackingService>();
                    var activeId = documentService.GetActiveDocument();
                    _currentDocument = activeId != null ? _solution.GetDocument(activeId) : null;
                }
60 61
            }

62
            internal async void Search()
63
            {
64 65
                try
                {
66
                    using (var navigateToSearch = Logger.LogBlock(FunctionId.NavigateTo_Search, KeyValueLogMessage.Create(LogType.UserAction), _cancellationToken))
67 68 69
                    using (var asyncToken = _asyncListener.BeginAsyncOperation(GetType() + ".Search"))
                    {
                        _progress.AddItems(_solution.Projects.Count());
70

71 72
                        // Search each project with an independent threadpool task.
                        var searchTasks = _solution.Projects.Select(
73
                            p => Task.Run(() => SearchAsync(p), _cancellationToken)).ToArray();
74

75 76 77
                        await Task.WhenAll(searchTasks).ConfigureAwait(false);
                    }
                }
78 79 80
                catch (OperationCanceledException)
                {
                }
81
                finally
82 83
                {
                    _callback.Done();
84
                }
85 86 87 88
            }

            private async Task SearchAsync(Project project)
            {
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
                try
                {
                    await SearchAsyncWorker(project).ConfigureAwait(false);
                }
                finally
                {
                    _progress.ItemCompleted();
                }
            }

            private async Task SearchAsyncWorker(Project project)
            {
                if (_searchCurrentDocument && _currentDocument?.Project != project)
                {
                    return;
                }

106 107 108 109 110
                var cacheService = project.Solution.Services.CacheService;
                if (cacheService != null)
                {
                    using (cacheService.EnableCaching(project.Id))
                    {
111
                        var service = TryGetNavigateToSearchService(project);
112 113
                        if (service != null)
                        {
114
                            var searchTask = _currentDocument != null
115 116
                                ? service.SearchDocumentAsync(_currentDocument, _searchPattern, _kinds, _cancellationToken)
                                : service.SearchProjectAsync(project, _searchPattern, _kinds, _cancellationToken);
117 118

                            var results = await searchTask.ConfigureAwait(false);
119 120 121 122 123 124 125 126 127 128 129 130 131 132
                            if (results != null)
                            {
                                foreach (var result in results)
                                {
                                    ReportMatchResult(project, result);
                                }
                            }
                        }
                    }
                }
            }

            private void ReportMatchResult(Project project, INavigateToSearchResult result)
            {
133 134 135 136 137
                var matchedSpans = result.NameMatchSpans.SelectAsArray(t => t.ToSpan());

                var patternMatch = new PatternMatch(GetPatternMatchKind(result.MatchKind), 
                    punctuationStripped: true, result.IsCaseSensitive, matchedSpans);

138 139 140 141 142 143
                var navigateToItem = new NavigateToItem(
                    result.Name,
                    result.Kind,
                    GetNavigateToLanguage(project.Language),
                    result.SecondarySort,
                    result,
144
                    patternMatch,
145
                    _displayFactory);
146 147 148
                _callback.AddItem(navigateToItem);
            }

149
            private PatternMatchKind GetPatternMatchKind(NavigateToMatchKind matchKind)
150 151 152
            {
                switch (matchKind)
                {
153 154 155 156 157 158 159 160 161 162 163 164
                    case NavigateToMatchKind.Exact: return PatternMatchKind.Exact;
                    case NavigateToMatchKind.Prefix: return PatternMatchKind.Prefix;
                    case NavigateToMatchKind.Substring: return PatternMatchKind.Substring;
                    case NavigateToMatchKind.Regular: return PatternMatchKind.Fuzzy;
                    case NavigateToMatchKind.None: return PatternMatchKind.Fuzzy;
                    case NavigateToMatchKind.CamelCaseExact: return PatternMatchKind.CamelCaseExact;
                    case NavigateToMatchKind.CamelCasePrefix: return PatternMatchKind.CamelCasePrefix;
                    case NavigateToMatchKind.CamelCaseNonContiguousPrefix: return PatternMatchKind.CamelCaseNonContiguousPrefix;
                    case NavigateToMatchKind.CamelCaseSubstring: return PatternMatchKind.CamelCaseSubstring;
                    case NavigateToMatchKind.CamelCaseNonContiguousSubstring: return PatternMatchKind.CamelCaseNonContiguousSubstring;
                    case NavigateToMatchKind.Fuzzy: return PatternMatchKind.Fuzzy;
                    default: throw ExceptionUtilities.UnexpectedValue(matchKind);
165 166 167
                }
            }

168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
            /// <summary>
            /// Returns the name for the language used by the old Navigate To providers.
            /// </summary>
            /// <remarks> It turns out this string is used for sorting and for some SQM data, so it's best
            /// to keep it unchanged.</remarks>
            private static string GetNavigateToLanguage(string languageName)
            {
                switch (languageName)
                {
                    case LanguageNames.CSharp:
                        return "csharp";
                    case LanguageNames.VisualBasic:
                        return "vb";
                    default:
                        return languageName;
                }
            }
        }
    }
}