VisualStudioSymbolSearchService.cs 7.9 KB
Newer Older
1 2 3 4
// 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;
5
using System.Collections.Immutable;
6 7 8 9 10
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
11
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
12 13
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Packaging;
T
Tomas Matousek 已提交
14
using Microsoft.CodeAnalysis.PooledObjects;
15 16 17
using Microsoft.CodeAnalysis.SymbolSearch;
using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem;
using Microsoft.VisualStudio.Settings;
18
using Microsoft.VisualStudio.Shell.Interop;
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
using Microsoft.VisualStudio.Shell.Settings;
using Roslyn.Utilities;
using VSShell = Microsoft.VisualStudio.Shell;

namespace Microsoft.VisualStudio.LanguageServices.SymbolSearch
{
    /// <summary>
    /// A service which enables searching for packages matching certain criteria.
    /// It works against an <see cref="Microsoft.CodeAnalysis.Elfie"/> database to find results.
    /// 
    /// This implementation also spawns a task which will attempt to keep that database up to
    /// date by downloading patches on a daily basis.
    /// </summary>
    [ExportWorkspaceService(typeof(ISymbolSearchService), ServiceLayer.Host), Shared]
    internal partial class VisualStudioSymbolSearchService : AbstractDelayStartedService, ISymbolSearchService
    {
35 36
        private readonly SemaphoreSlim _gate = new SemaphoreSlim(initialCount: 1);
        private ISymbolSearchUpdateEngine _updateEngine;
37

38
        private readonly VisualStudioWorkspaceImpl _workspace;
39 40
        private readonly IPackageInstallerService _installerService;
        private readonly string _localSettingsDirectory;
C
CyrusNajmabadi 已提交
41
        private readonly LogService _logService;
42
        private readonly ISymbolSearchProgressService _progressService;
43 44

        private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
45 46

        [ImportingConstructor]
47
        [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
48
        public VisualStudioSymbolSearchService(
49
            IThreadingContext threadingContext,
50 51
            VisualStudioWorkspaceImpl workspace,
            VSShell.SVsServiceProvider serviceProvider)
52
            : base(threadingContext, workspace, SymbolSearchOptions.Enabled,
53 54 55
                              SymbolSearchOptions.SuggestForTypesInReferenceAssemblies,
                              SymbolSearchOptions.SuggestForTypesInNuGetPackages)
        {
56
            _workspace = workspace;
57
            _installerService = workspace.Services.GetService<IPackageInstallerService>();
58
            _localSettingsDirectory = new ShellSettingsManager(serviceProvider).GetApplicationDataFolder(ApplicationDataFolder.LocalSettings);
59

60
            _logService = new LogService(threadingContext, (IVsActivityLog)serviceProvider.GetService(typeof(SVsActivityLog)));
61
            _progressService = workspace.Services.GetService<ISymbolSearchProgressService>();
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
        }

        protected override void EnableService()
        {
            // When our service is enabled hook up to package source changes.
            // We need to know when the list of sources have changed so we can
            // kick off the work to process them.
            _installerService.PackageSourcesChanged += OnPackageSourcesChanged;
        }

        private void OnPackageSourcesChanged(object sender, EventArgs e)
        {
            StartWorking();
        }

        protected override void StartWorking()
        {
            // Always pull down the nuget.org index.  It contains the MS reference assembly index
            // inside of it.
81
            Task.Run(() => UpdateSourceInBackgroundAsync(SymbolSearchUpdateEngine.NugetOrgSource));
82 83
        }

84
        private async Task<ISymbolSearchUpdateEngine> GetEngineAsync(CancellationToken cancellationToken)
85 86 87 88 89
        {
            using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
            {
                if (_updateEngine == null)
                {
90
                    _updateEngine = await SymbolSearchUpdateEngineFactory.CreateEngineAsync(
91
                        _workspace, _logService, _progressService, cancellationToken).ConfigureAwait(false);
92 93 94 95 96 97
                }

                return _updateEngine;
            }
        }

98
        private async Task UpdateSourceInBackgroundAsync(string sourceName)
99
        {
100
            var engine = await GetEngineAsync(_cancellationTokenSource.Token).ConfigureAwait(false);
101
            await engine.UpdateContinuouslyAsync(sourceName, _localSettingsDirectory).ConfigureAwait(false);
102 103
        }

H
Heejae Chang 已提交
104
        public async Task<IList<PackageWithTypeResult>> FindPackagesWithTypeAsync(
105 106
            string source, string name, int arity, CancellationToken cancellationToken)
        {
107
            var engine = await GetEngineAsync(cancellationToken).ConfigureAwait(false);
108
            var allPackagesWithType = await engine.FindPackagesWithTypeAsync(
109
                source, name, arity, cancellationToken).ConfigureAwait(false);
110

111 112 113
            return FilterAndOrderPackages(allPackagesWithType);
        }

H
Heejae Chang 已提交
114
        public async Task<IList<PackageWithAssemblyResult>> FindPackagesWithAssemblyAsync(
115 116
            string source, string assemblyName, CancellationToken cancellationToken)
        {
117
            var engine = await GetEngineAsync(cancellationToken).ConfigureAwait(false);
118
            var allPackagesWithAssembly = await engine.FindPackagesWithAssemblyAsync(
119
                source, assemblyName, cancellationToken).ConfigureAwait(false);
120 121 122 123 124 125 126 127 128

            return FilterAndOrderPackages(allPackagesWithAssembly);
        }

        private ImmutableArray<TPackageResult> FilterAndOrderPackages<TPackageResult>(
            ImmutableArray<TPackageResult> allPackages) where TPackageResult : PackageResult
        {
            var packagesUsedInOtherProjects = new List<TPackageResult>();
            var packagesNotUsedInOtherProjects = new List<TPackageResult>();
129

130
            foreach (var package in allPackages)
131
            {
132 133 134
                var resultList = _installerService.GetInstalledVersions(package.PackageName).Any()
                    ? packagesUsedInOtherProjects
                    : packagesNotUsedInOtherProjects;
135

136
                resultList.Add(package);
137 138
            }

139
            var result = ArrayBuilder<TPackageResult>.GetInstance();
140

J
John Doe 已提交
141
            // We always return types from packages that we've use elsewhere in the project.
142
            result.AddRange(packagesUsedInOtherProjects);
143 144 145 146 147 148

            // For all other hits include as long as the popularity is high enough.  
            // Popularity ranks are in powers of two.  So if two packages differ by 
            // one rank, then one is at least twice as popular as the next.  Two 
            // ranks would be four times as popular.  Three ranks = 8 times,  etc. 
            // etc.  We keep packages that within 1 rank of the best package we find.
149
            int? bestRank = packagesUsedInOtherProjects.LastOrDefault()?.Rank;
150
            foreach (var packageWithType in packagesNotUsedInOtherProjects)
151 152 153 154 155 156
            {
                var rank = packageWithType.Rank;
                bestRank = bestRank == null ? rank : Math.Max(bestRank.Value, rank);

                if (Math.Abs(bestRank.Value - rank) > 1)
                {
157
                    break;
158 159
                }

160
                result.Add(packageWithType);
161
            }
162 163

            return result.ToImmutableAndFree();
164 165
        }

H
Heejae Chang 已提交
166
        public async Task<IList<ReferenceAssemblyWithTypeResult>> FindReferenceAssembliesWithTypeAsync(
167 168
            string name, int arity, CancellationToken cancellationToken)
        {
169
            var engine = await GetEngineAsync(cancellationToken).ConfigureAwait(false);
170
            return await engine.FindReferenceAssembliesWithTypeAsync(
171
                name, arity, cancellationToken).ConfigureAwait(false);
172 173
        }
    }
174
}