File: Services\MissingImportDiscovery\RemoteMissingImportDiscoveryService.cs
Web Access
Project: ..\..\..\src\Workspaces\Remote\ServiceHub\Microsoft.CodeAnalysis.Remote.ServiceHub.csproj (Microsoft.CodeAnalysis.Remote.ServiceHub)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.AddImport;
using Microsoft.CodeAnalysis.Packaging;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.SymbolSearch;
using Microsoft.CodeAnalysis.Text;
 
namespace Microsoft.CodeAnalysis.Remote
{
    internal sealed class RemoteMissingImportDiscoveryService : BrokeredServiceBase, IRemoteMissingImportDiscoveryService
    {
        internal sealed class Factory : FactoryBase<IRemoteMissingImportDiscoveryService, IRemoteMissingImportDiscoveryService.ICallback>
        {
            protected override IRemoteMissingImportDiscoveryService CreateService(in ServiceConstructionArguments arguments, RemoteCallback<IRemoteMissingImportDiscoveryService.ICallback> callback)
                => new RemoteMissingImportDiscoveryService(arguments, callback);
        }
 
        private readonly RemoteCallback<IRemoteMissingImportDiscoveryService.ICallback> _callback;
 
        public RemoteMissingImportDiscoveryService(in ServiceConstructionArguments arguments, RemoteCallback<IRemoteMissingImportDiscoveryService.ICallback> callback)
            : base(arguments)
        {
            _callback = callback;
        }
 
        public ValueTask<ImmutableArray<AddImportFixData>> GetFixesAsync(
            Checksum solutionChecksum,
            RemoteServiceCallbackId callbackId,
            DocumentId documentId,
            TextSpan span,
            string diagnosticId,
            int maxResults,
            AddImportOptions options,
            ImmutableArray<PackageSource> packageSources,
            CancellationToken cancellationToken)
        {
            return RunServiceAsync(solutionChecksum, async solution =>
            {
                var document = solution.GetRequiredDocument(documentId);
 
                var service = document.GetRequiredLanguageService<IAddImportFeatureService>();
 
                var symbolSearchService = new SymbolSearchService(_callback, callbackId);
 
                var result = await service.GetFixesAsync(
                    document, span, diagnosticId, maxResults,
                    symbolSearchService, options,
                    packageSources, cancellationToken).ConfigureAwait(false);
 
                return result;
            }, cancellationToken);
        }
 
        public ValueTask<ImmutableArray<AddImportFixData>> GetUniqueFixesAsync(
            Checksum solutionChecksum,
            RemoteServiceCallbackId callbackId,
            DocumentId documentId,
            TextSpan span,
            ImmutableArray<string> diagnosticIds,
            AddImportOptions options,
            ImmutableArray<PackageSource> packageSources,
            CancellationToken cancellationToken)
        {
            return RunServiceAsync(solutionChecksum, async solution =>
            {
                var document = solution.GetRequiredDocument(documentId);
 
                var service = document.GetRequiredLanguageService<IAddImportFeatureService>();
 
                var symbolSearchService = new SymbolSearchService(_callback, callbackId);
 
                var result = await service.GetUniqueFixesAsync(
                    document, span, diagnosticIds,
                    symbolSearchService, options,
                    packageSources, cancellationToken).ConfigureAwait(false);
 
                return result;
            }, cancellationToken);
        }
 
        /// <summary>
        /// Provides an implementation of the <see cref="ISymbolSearchService"/> on the remote side so that
        /// Add-Import can find results in nuget packages/reference assemblies.  This works
        /// by remoting *from* the OOP server back to the host, which can then forward this 
        /// appropriately to wherever the real <see cref="ISymbolSearchService"/> is running.  This is necessary
        /// because it's not guaranteed that the real <see cref="ISymbolSearchService"/> will be running in 
        /// the same process that is supplying the <see cref="RemoteMissingImportDiscoveryService"/>.
        /// 
        /// Ideally we would not need to bounce back to the host for this.
        /// </summary>
        private sealed class SymbolSearchService : ISymbolSearchService
        {
            private readonly RemoteCallback<IRemoteMissingImportDiscoveryService.ICallback> _callback;
            private readonly RemoteServiceCallbackId _callbackId;
 
            public SymbolSearchService(RemoteCallback<IRemoteMissingImportDiscoveryService.ICallback> callback, RemoteServiceCallbackId callbackId)
            {
                _callback = callback;
                _callbackId = callbackId;
            }
 
            public ValueTask<ImmutableArray<PackageWithTypeResult>> FindPackagesWithTypeAsync(string source, string name, int arity, CancellationToken cancellationToken)
                => _callback.InvokeAsync((callback, cancellationToken) => callback.FindPackagesWithTypeAsync(_callbackId, source, name, arity, cancellationToken), cancellationToken);
 
            public ValueTask<ImmutableArray<PackageWithAssemblyResult>> FindPackagesWithAssemblyAsync(string source, string assemblyName, CancellationToken cancellationToken)
                => _callback.InvokeAsync((callback, cancellationToken) => callback.FindPackagesWithAssemblyAsync(_callbackId, source, assemblyName, cancellationToken), cancellationToken);
 
            public ValueTask<ImmutableArray<ReferenceAssemblyWithTypeResult>> FindReferenceAssembliesWithTypeAsync(string name, int arity, CancellationToken cancellationToken)
                => _callback.InvokeAsync((callback, cancellationToken) => callback.FindReferenceAssembliesWithTypeAsync(_callbackId, name, arity, cancellationToken), cancellationToken);
        }
    }
}