提交 8cda7de6 编写于 作者: C CyrusNajmabadi 提交者: GitHub

Merge pull request #19997 from CyrusNajmabadi/addUsingOOP10

Add the remote endpoint for Add-Import and hook up to VS.
......@@ -8,10 +8,12 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Experiments;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Packaging;
using Microsoft.CodeAnalysis.Remote;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.SymbolSearch;
using Microsoft.CodeAnalysis.Text;
......@@ -56,6 +58,30 @@ protected AbstractAddImportFeatureService()
Document document, TextSpan span, string diagnosticId, ISymbolSearchService symbolSearchService,
bool searchReferenceAssemblies, ImmutableArray<PackageSource> packageSources,
CancellationToken cancellationToken)
{
var session = await document.Project.Solution.TryCreateCodeAnalysisServiceSessionAsync(
AddImportOptions.OutOfProcessAllowed, WellKnownExperimentNames.RoslynFeatureOOP,
new RemoteSymbolSearchService(symbolSearchService, cancellationToken), cancellationToken).ConfigureAwait(false);
using (session)
{
if (session == null)
{
return await GetFixesInCurrentProcessAsync(
document, span, diagnosticId, symbolSearchService,
searchReferenceAssemblies, packageSources, cancellationToken).ConfigureAwait(false);
}
else
{
return await GetFixesInRemoteProcessAsync(
session, document, span, diagnosticId,
searchReferenceAssemblies, packageSources).ConfigureAwait(false);
}
}
}
private async Task<ImmutableArray<AddImportFixData>> GetFixesInCurrentProcessAsync(
Document document, TextSpan span, string diagnosticId, ISymbolSearchService symbolSearchService,
bool searchReferenceAssemblies, ImmutableArray<PackageSource> packageSources, CancellationToken cancellationToken)
{
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var node = root.FindToken(span.Start, findInsideTrivia: true)
......@@ -121,7 +147,7 @@ protected AbstractAddImportFeatureService()
// things like the Interactive workspace as this will cause us to
// create expensive bk-trees which we won't even be able to save for
// future use.
if (!IsHostOrTestWorkspace(project))
if (!IsHostOrTestOrRemoteWorkspace(project))
{
return ImmutableArray<Reference>.Empty;
}
......@@ -130,10 +156,11 @@ protected AbstractAddImportFeatureService()
return fuzzyReferences;
}
private static bool IsHostOrTestWorkspace(Project project)
private static bool IsHostOrTestOrRemoteWorkspace(Project project)
{
return project.Solution.Workspace.Kind == WorkspaceKind.Host ||
project.Solution.Workspace.Kind == WorkspaceKind.Test;
project.Solution.Workspace.Kind == WorkspaceKind.Test ||
project.Solution.Workspace.Kind == WorkspaceKind.RemoteWorkspace;
}
private async Task<ImmutableArray<Reference>> FindResultsAsync(
......@@ -152,7 +179,7 @@ private static bool IsHostOrTestWorkspace(Project project)
// things like the Interactive workspace as we can't even add project
// references to the interactive window. We could consider adding metadata
// references with #r in the future.
if (IsHostOrTestWorkspace(project))
if (IsHostOrTestOrRemoteWorkspace(project))
{
// Now search unreferenced projects, and see if they have any source symbols that match
// the search string.
......
// 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.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.Tags;
using Microsoft.CodeAnalysis.Text;
......
// 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.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Packaging;
using Microsoft.CodeAnalysis.Remote;
using Microsoft.CodeAnalysis.SymbolSearch;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.AddImport
{
internal abstract partial class AbstractAddImportFeatureService<TSimpleNameSyntax>
{
private async Task<ImmutableArray<AddImportFixData>> GetFixesInRemoteProcessAsync(
RemoteHostClient.Session session, Document document, TextSpan span, string diagnosticId,
bool searchReferenceAssemblies, ImmutableArray<PackageSource> packageSources)
{
var result = await session.InvokeAsync<ImmutableArray<AddImportFixData>>(
nameof(IRemoteAddImportFeatureService.GetFixesAsync),
new object[] { document.Id, span, diagnosticId, searchReferenceAssemblies, packageSources }).ConfigureAwait(false);
return result;
}
/// <summary>
/// Used to supply the OOP server a callback that it can use to search for ReferenceAssemblies or
/// nuget packages. We can't necessarily do that search directly in the OOP server as our
/// 'SymbolSearchEngine' may actually be running in a *different* process (there is no guarantee
/// that all remote work happens in the same process).
///
/// This does mean, currently, that when we call over to OOP to do a search, it will bounce
/// back to VS, which will then bounce back out to OOP to perform the Nuget/ReferenceAssembly
/// portion of the search. Ideally we could keep this all OOP.
/// </summary>
private class RemoteSymbolSearchService : IRemoteSymbolSearchUpdateEngine
{
private readonly ISymbolSearchService _symbolSearchService;
private readonly CancellationToken _cancellationToken;
public RemoteSymbolSearchService(
ISymbolSearchService symbolSearchService,
CancellationToken cancellationToken)
{
_symbolSearchService = symbolSearchService;
_cancellationToken = cancellationToken;
}
public Task UpdateContinuouslyAsync(string sourceName, string localSettingsDirectory)
{
// Remote side should never call this.
throw new NotImplementedException();
}
public async Task<ImmutableArray<PackageWithTypeResult>> FindPackagesWithTypeAsync(
string source, string name, int arity)
{
var result = await _symbolSearchService.FindPackagesWithTypeAsync(
source, name, arity, _cancellationToken).ConfigureAwait(false);
return result;
}
public async Task<ImmutableArray<PackageWithAssemblyResult>> FindPackagesWithAssemblyAsync(
string source, string name)
{
var result = await _symbolSearchService.FindPackagesWithAssemblyAsync(
source, name, _cancellationToken).ConfigureAwait(false);
return result;
}
public async Task<ImmutableArray<ReferenceAssemblyWithTypeResult>> FindReferenceAssembliesWithTypeAsync(
string name, int arity)
{
var result = await _symbolSearchService.FindReferenceAssembliesWithTypeAsync(
name, arity, _cancellationToken).ConfigureAwait(false);
return result;
}
}
}
}
\ No newline at end of file
// 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.Collections.Immutable;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Packaging;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.AddImport
{
internal interface IRemoteAddImportFeatureService
{
Task<ImmutableArray<AddImportFixData>> GetFixesAsync(
DocumentId documentId, TextSpan span, string diagnosticId,
bool searchReferenceAssemblies, ImmutableArray<PackageSource> packageSources);
}
}
\ No newline at end of file
......@@ -100,6 +100,7 @@
<Link>Shared\Utilities\DesktopShim.cs</Link>
</Compile>
<Compile Include="AddImport\AbstractAddImportFeatureService.cs" />
<Compile Include="AddImport\Remote\AbstractAddImportFeatureService_Remote.cs" />
<Compile Include="AddImport\AddImportFixData.cs" />
<Compile Include="AddImport\AddImportFixKind.cs" />
<Compile Include="AddImport\AddImportOptions.cs" />
......@@ -108,6 +109,7 @@
<Compile Include="AddImport\CodeActions\MetadataSymbolReferenceCodeAction.cs" />
<Compile Include="AddImport\CodeActions\ProjectSymbolReferenceCodeAction.cs" />
<Compile Include="AddImport\IAddImportFeatureService.cs" />
<Compile Include="AddImport\Remote\IRemoteAddImportFeatureService.cs" />
<Compile Include="AddImport\SearchScopes\AllSymbolsProjectSearchScope.cs" />
<Compile Include="AddImport\SearchScopes\MetadataSymbolsSearchScope.cs" />
<Compile Include="AddImport\SearchScopes\ProjectSearchScope.cs" />
......
......@@ -57,17 +57,17 @@ public static bool IsOutOfProcessEnabled(this Workspace workspace, Option<bool>
return false;
}
// Treat experiments as always on in tests.
if (experimentName != null && workspace.Kind != WorkspaceKind.Test)
{
var experimentEnabled = workspace.Services.GetService<IExperimentationService>();
if (!experimentEnabled.IsExperimentEnabled(experimentName))
{
return false;
}
}
return true;
// Treat experiments as always on in tests.
if (experimentName != null && workspace.Kind != WorkspaceKind.Test)
{
var experimentEnabled = workspace.Services.GetService<IExperimentationService>();
if (!experimentEnabled.IsExperimentEnabled(experimentName))
{
return false;
}
}
return true;
}
public static async Task<RemoteHostClient.Session> TryCreateCodeAnalysisServiceSessionAsync(
......@@ -86,7 +86,7 @@ public static bool IsOutOfProcessEnabled(this Workspace workspace, Option<bool>
return null;
}
return await client.TryCreateCodeAnalysisServiceSessionAsync(solution, cancellationToken).ConfigureAwait(false);
return await client.TryCreateCodeAnalysisServiceSessionAsync(solution, callbackTarget, cancellationToken).ConfigureAwait(false);
}
public static Task RunOnRemoteHostAsync(
......
......@@ -74,6 +74,7 @@
<Compile Include="Services\CodeAnalysisService_CodeLens.cs" />
<Compile Include="Services\CodeAnalysisService_DesignerAttributes.cs" />
<Compile Include="Services\CodeAnalysisService_DocumentHighlights.cs" />
<Compile Include="Services\CodeAnalysisService_AddImport.cs" />
<Compile Include="Services\CodeAnalysisService_TodoComments.cs" />
<Compile Include="Services\CodeAnalysisService_SymbolFinder.cs" />
<Compile Include="Services\RemoteSymbolSearchUpdateEngine.cs" />
......
// 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.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 partial class CodeAnalysisService : IRemoteAddImportFeatureService
{
public async Task<ImmutableArray<AddImportFixData>> GetFixesAsync(
DocumentId documentId, TextSpan span, string diagnosticId,
bool searchReferenceAssemblies, ImmutableArray<PackageSource> packageSources)
{
using (UserOperationBooster.Boost())
{
var solution = await GetSolutionAsync().ConfigureAwait(false);
var document = solution.GetDocument(documentId);
var service = document.GetLanguageService<IAddImportFeatureService>();
var symbolSearchService = new SymbolSearchService(this);
var result = await service.GetFixesAsync(
document, span, diagnosticId, symbolSearchService, searchReferenceAssemblies,
packageSources, CancellationToken).ConfigureAwait(false);
return result;
}
}
/// <summary>
/// Provides an implementation of the 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 ISymbolSearchService is running. This is necessary
/// because it's not guaranteed that the real ISymbolSearchService will be running in
/// the same process that is supplying the <see cref="CodeAnalysisService"/>.
///
/// Ideally we would not need to bounce back to the host for this.
/// </summary>
private class SymbolSearchService : ISymbolSearchService
{
private readonly CodeAnalysisService codeAnalysisService;
public SymbolSearchService(CodeAnalysisService codeAnalysisService)
{
this.codeAnalysisService = codeAnalysisService;
}
public async Task<ImmutableArray<PackageWithTypeResult>> FindPackagesWithTypeAsync(
string source, string name, int arity, CancellationToken cancellationToken)
{
var result = await codeAnalysisService.Rpc.InvokeAsync<ImmutableArray<PackageWithTypeResult>>(
nameof(FindPackagesWithTypeAsync), source, name, arity).ConfigureAwait(false);
return result;
}
public async Task<ImmutableArray<PackageWithAssemblyResult>> FindPackagesWithAssemblyAsync(
string source, string assemblyName, CancellationToken cancellationToken)
{
var result = await codeAnalysisService.Rpc.InvokeAsync<ImmutableArray<PackageWithAssemblyResult>>(
nameof(FindPackagesWithAssemblyAsync), source, assemblyName).ConfigureAwait(false);
return result;
}
public async Task<ImmutableArray<ReferenceAssemblyWithTypeResult>> FindReferenceAssembliesWithTypeAsync(
string name, int arity, CancellationToken cancellationToken)
{
var result = await codeAnalysisService.Rpc.InvokeAsync<ImmutableArray<ReferenceAssemblyWithTypeResult>>(
nameof(FindReferenceAssembliesWithTypeAsync), name, arity).ConfigureAwait(false);
return result;
}
}
}
}
\ No newline at end of file
......@@ -3,6 +3,8 @@
using System;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.AddImport;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.DocumentHighlighting;
using Microsoft.CodeAnalysis.Packaging;
using Microsoft.CodeAnalysis.SymbolSearch;
......@@ -27,6 +29,8 @@ internal partial class AggregateJsonConverter : JsonConverter
Add(builder, new PackageWithTypeResultJsonConverter());
Add(builder, new PackageWithAssemblyResultJsonConverter());
Add(builder, new ReferenceAssemblyWithTypeResultJsonConverter());
Add(builder, new AddImportFixDataJsonConverter());
}
private class TodoCommentDescriptorJsonConverter : BaseJsonConverter<TodoCommentDescriptor>
......@@ -286,5 +290,97 @@ protected override void WriteValue(JsonWriter writer, TaggedText source, JsonSer
writer.WriteEndObject();
}
}
private class AddImportFixDataJsonConverter : BaseJsonConverter<AddImportFixData>
{
protected override AddImportFixData ReadValue(JsonReader reader, JsonSerializer serializer)
{
Contract.ThrowIfFalse(reader.TokenType == JsonToken.StartObject);
var kind = (AddImportFixKind)ReadProperty<long>(reader);
var textChanges = ReadProperty<ImmutableArray<TextChange>>(serializer, reader);
var title = ReadProperty<string>(reader);
var tags = ReadProperty<ImmutableArray<string>>(serializer, reader);
var priority = (CodeActionPriority)ReadProperty<long>(reader);
var projectReferenceToAdd = ReadProperty<ProjectId>(serializer, reader);
var portableExecutableReferenceProjectId = ReadProperty<ProjectId>(serializer, reader);
var portableExecutableReferenceFilePathToAdd = ReadProperty<string>(reader);
var assemblyReferenceAssemblyName = ReadProperty<string>(reader);
var assemblyReferenceFullyQualifiedTypeName = ReadProperty<string>(reader);
var packageSource = ReadProperty<string>(reader);
var packageName = ReadProperty<string>(reader);
var packageVersionOpt = ReadProperty<string>(reader);
Contract.ThrowIfFalse(reader.Read());
Contract.ThrowIfFalse(reader.TokenType == JsonToken.EndObject);
switch (kind)
{
case AddImportFixKind.ProjectSymbol:
return AddImportFixData.CreateForProjectSymbol(textChanges, title, tags, priority, projectReferenceToAdd);
case AddImportFixKind.MetadataSymbol:
return AddImportFixData.CreateForMetadataSymbol(textChanges, title, tags, priority, portableExecutableReferenceProjectId, portableExecutableReferenceFilePathToAdd);
case AddImportFixKind.PackageSymbol:
return AddImportFixData.CreateForPackageSymbol(textChanges, packageSource, packageName, packageVersionOpt);
case AddImportFixKind.ReferenceAssemblySymbol:
return AddImportFixData.CreateForReferenceAssemblySymbol(textChanges, title, assemblyReferenceAssemblyName, assemblyReferenceFullyQualifiedTypeName);
}
throw ExceptionUtilities.Unreachable;
}
protected override void WriteValue(JsonWriter writer, AddImportFixData source, JsonSerializer serializer)
{
writer.WriteStartObject();
writer.WritePropertyName(nameof(AddImportFixData.Kind));
writer.WriteValue((int)source.Kind);
writer.WritePropertyName(nameof(AddImportFixData.TextChanges));
serializer.Serialize(writer, source.TextChanges);
writer.WritePropertyName(nameof(AddImportFixData.Title));
writer.WriteValue(source.Title);
writer.WritePropertyName(nameof(AddImportFixData.Tags));
serializer.Serialize(writer, source.Tags.NullToEmpty());
writer.WritePropertyName(nameof(AddImportFixData.Priority));
writer.WriteValue((int)source.Priority);
writer.WritePropertyName(nameof(AddImportFixData.ProjectReferenceToAdd));
serializer.Serialize(writer, source.ProjectReferenceToAdd);
writer.WritePropertyName(nameof(AddImportFixData.PortableExecutableReferenceProjectId));
serializer.Serialize(writer, source.PortableExecutableReferenceProjectId);
writer.WritePropertyName(nameof(AddImportFixData.PortableExecutableReferenceFilePathToAdd));
writer.WriteValue(source.PortableExecutableReferenceFilePathToAdd);
writer.WritePropertyName(nameof(AddImportFixData.AssemblyReferenceAssemblyName));
writer.WriteValue(source.AssemblyReferenceAssemblyName);
writer.WritePropertyName(nameof(AddImportFixData.AssemblyReferenceFullyQualifiedTypeName));
writer.WriteValue(source.AssemblyReferenceFullyQualifiedTypeName);
writer.WritePropertyName(nameof(AddImportFixData.PackageSource));
writer.WriteValue(source.PackageSource);
writer.WritePropertyName(nameof(AddImportFixData.PackageName));
writer.WriteValue(source.PackageName);
writer.WritePropertyName(nameof(AddImportFixData.PackageVersionOpt));
writer.WriteValue(source.PackageVersionOpt);
writer.WriteEndObject();
}
}
}
}
\ No newline at end of file
......@@ -10,8 +10,13 @@ internal partial class AggregateJsonConverter : JsonConverter
{
private abstract class WorkspaceIdJsonConverter<T> : BaseJsonConverter<T>
{
protected (Guid, string) ReadFromJsonObject(JsonReader reader)
protected (Guid, string)? ReadFromJsonObject(JsonReader reader)
{
if (reader.TokenType == JsonToken.Null)
{
return null;
}
Contract.ThrowIfFalse(reader.TokenType == JsonToken.StartObject);
var (id, debugName) = ReadIdAndName(reader);
......@@ -51,8 +56,8 @@ private class SolutionIdJsonConverter : WorkspaceIdJsonConverter<SolutionId>
{
protected override SolutionId ReadValue(JsonReader reader, JsonSerializer serializer)
{
var (id, debugName) = ReadFromJsonObject(reader);
return SolutionId.CreateFromSerialized(id, debugName);
(Guid id, string debugName)? tuple = ReadFromJsonObject(reader);
return tuple == null ? null : SolutionId.CreateFromSerialized(tuple.Value.id, tuple.Value.debugName);
}
protected override void WriteValue(JsonWriter writer, SolutionId solutionId, JsonSerializer serializer)
......@@ -63,8 +68,8 @@ private class ProjectIdJsonConverter : WorkspaceIdJsonConverter<ProjectId>
{
protected override ProjectId ReadValue(JsonReader reader, JsonSerializer serializer)
{
var (id, debugName) = ReadFromJsonObject(reader);
return ProjectId.CreateFromSerialized(id, debugName);
(Guid id, string debugName)? tuple = ReadFromJsonObject(reader);
return tuple == null ? null : ProjectId.CreateFromSerialized(tuple.Value.id, tuple.Value.debugName);
}
protected override void WriteValue(JsonWriter writer, ProjectId projectId, JsonSerializer serializer)
......@@ -75,6 +80,11 @@ private class DocumentIdJsonConverter : WorkspaceIdJsonConverter<DocumentId>
{
protected override DocumentId ReadValue(JsonReader reader, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return null;
}
Contract.ThrowIfFalse(reader.TokenType == JsonToken.StartObject);
var projectId = ReadProperty<ProjectId>(serializer, reader);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册