提交 e0747c42 编写于 作者: C Cyrus Najmabadi

Move more of the find-references user-facing operation OOP.

上级 d581572e
......@@ -6,7 +6,6 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Classification;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.CodeAnalysis.Navigation;
......@@ -49,7 +48,7 @@ public async Task OnReferenceFoundAsync(Document document, TextSpan span)
/// <summary>
/// Forwards IFindReferencesProgress calls to an IFindUsagesContext instance.
/// </summary>
private class FindReferencesProgressAdapter : ForegroundThreadAffinitizedObject, IStreamingFindReferencesProgress
private class FindReferencesProgressAdapter : IStreamingFindReferencesProgress
{
private readonly Solution _solution;
private readonly IFindUsagesContext _context;
......@@ -74,9 +73,7 @@ public IStreamingProgressTracker ProgressTracker
=> _context.ProgressTracker;
public FindReferencesProgressAdapter(
IThreadingContext threadingContext, Solution solution,
IFindUsagesContext context, FindReferencesSearchOptions options)
: base(threadingContext)
Solution solution, IFindUsagesContext context, FindReferencesSearchOptions options)
{
_solution = solution;
_context = context;
......
......@@ -21,8 +21,7 @@ internal abstract partial class AbstractFindUsagesService
private static async Task FindSymbolMonikerReferencesAsync(
IFindSymbolMonikerUsagesService monikerUsagesService,
ISymbol definition,
IFindUsagesContext context,
CancellationToken cancellationToken)
IFindUsagesContext context)
{
var moniker = SymbolMoniker.TryCreate(definition);
if (moniker == null)
......@@ -42,7 +41,7 @@ internal abstract partial class AbstractFindUsagesService
var monikers = ImmutableArray.Create(moniker);
var first = true;
await foreach (var referenceItem in monikerUsagesService.FindReferencesByMoniker(definitionItem, monikers, cancellationToken))
await foreach (var referenceItem in monikerUsagesService.FindReferencesByMoniker(definitionItem, monikers, context.CancellationToken))
{
if (first)
{
......
......@@ -13,6 +13,7 @@
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Remote;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
......@@ -121,17 +122,15 @@ protected AbstractFindUsagesService(IThreadingContext threadingContext)
cancellationToken.ThrowIfCancellationRequested();
// Find the symbol we want to search and the solution we want to search in.
var symbolAndSolutionOpt = await FindUsagesHelpers.GetRelevantSymbolAndSolutionAtPositionAsync(
var symbolAndProjectOpt = await FindUsagesHelpers.GetRelevantSymbolAndProjectAtPositionAsync(
document, position, cancellationToken).ConfigureAwait(false);
if (symbolAndSolutionOpt == null)
if (symbolAndProjectOpt == null)
return;
var (symbol, solution) = symbolAndSolutionOpt.Value;
var (symbol, project) = symbolAndProjectOpt.Value;
await FindSymbolReferencesAsync(
_threadingContext, context,
symbol, solution,
cancellationToken).ConfigureAwait(false);
context, symbol, project).ConfigureAwait(false);
}
/// <summary>
......@@ -139,9 +138,9 @@ protected AbstractFindUsagesService(IThreadingContext threadingContext)
/// and want to push all the references to it into the Streaming-Find-References window.
/// </summary>
public static async Task FindSymbolReferencesAsync(
IThreadingContext threadingContext, IFindUsagesContext context,
ISymbol symbol, Solution solution, CancellationToken cancellationToken)
IFindUsagesContext context, ISymbol symbol, Project project)
{
var solution = project.Solution;
var monikerUsagesService = solution.Workspace.Services.GetRequiredService<IFindSymbolMonikerUsagesService>();
await context.SetSearchTitleAsync(string.Format(EditorFeaturesResources._0_references,
......@@ -153,17 +152,64 @@ protected AbstractFindUsagesService(IThreadingContext threadingContext)
// engine will push results into the 'progress' instance passed into it.
// We'll take those results, massage them, and forward them along to the
// FindReferencesContext instance we were given.
var progress = new FindReferencesProgressAdapter(threadingContext, solution, context, options);
var normalFindReferencesTask = SymbolFinder.FindReferencesAsync(
symbol, solution, progress, documents: null, options, cancellationToken);
var normalFindReferencesTask = FindReferencesAsync(
context, symbol, project, options);
// Kick off work to search the online code index system in parallel
var codeIndexReferencesTask = FindSymbolMonikerReferencesAsync(
monikerUsagesService, symbol, context, cancellationToken);
monikerUsagesService, symbol, context);
await Task.WhenAll(normalFindReferencesTask, codeIndexReferencesTask).ConfigureAwait(false);
}
public static async Task FindReferencesAsync(
IFindUsagesContext context,
ISymbol symbol,
Project project,
FindReferencesSearchOptions options)
{
var cancellationToken = context.CancellationToken;
var solution = project.Solution;
var client = await RemoteHostClient.TryGetClientAsync(solution.Workspace, cancellationToken).ConfigureAwait(false);
if (client != null)
{
// Create a callback that we can pass to the server process to hear about the
// results as it finds them. When we hear about results we'll forward them to
// the 'progress' parameter which will then update the UI.
var serverCallback = new FindReferencesServerCallback(solution, context);
var success = await client.TryRunRemoteAsync(
WellKnownServiceHubServices.CodeAnalysisService,
nameof(IRemoteFindUsagesService.FindReferencesAsync),
solution,
new object[]
{
SerializableSymbolAndProjectId.Create(symbol, project, cancellationToken),
SerializableFindReferencesSearchOptions.Dehydrate(options),
},
serverCallback,
cancellationToken).ConfigureAwait(false);
if (success)
return;
}
// Couldn't effectively search in OOP. Perform the search in-proc.
await FindReferencesInCurrentProcessAsync(
context, symbol, project, options).ConfigureAwait(false);
}
private static Task FindReferencesInCurrentProcessAsync(
IFindUsagesContext context,
ISymbol symbol,
Project project,
FindReferencesSearchOptions options)
{
var progress = new FindReferencesProgressAdapter(project.Solution, context, options);
return SymbolFinder.FindReferencesAsync(
symbol, project.Solution, progress, documents: null, options, context.CancellationToken);
}
private async Task<bool> TryFindLiteralReferencesAsync(
Document document, int position, IFindUsagesContext context)
{
......
......@@ -31,7 +31,7 @@ public static string GetDisplayName(ISymbol symbol)
/// there may be symbol mapping involved (for example in Metadata-As-Source
/// scenarios).
/// </summary>
public static async Task<(ISymbol symbol, Solution solution)?> GetRelevantSymbolAndSolutionAtPositionAsync(
public static async Task<(ISymbol symbol, Project project)?> GetRelevantSymbolAndProjectAtPositionAsync(
Document document, int position, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
......@@ -49,19 +49,19 @@ public static string GetDisplayName(ISymbol symbol)
if (mapping == null)
return null;
return (mapping.Symbol, mapping.Project.Solution);
return (mapping.Symbol, mapping.Project);
}
public static async Task<(Solution solution, ISymbol symbol, ImmutableArray<ISymbol> implementations, string message)?> FindSourceImplementationsAsync(Document document, int position, CancellationToken cancellationToken)
{
var symbolAndSolutionOpt = await GetRelevantSymbolAndSolutionAtPositionAsync(
var symbolAndProjectOpt = await GetRelevantSymbolAndProjectAtPositionAsync(
document, position, cancellationToken).ConfigureAwait(false);
if (symbolAndSolutionOpt == null)
if (symbolAndProjectOpt == null)
return null;
var (symbol, solution) = symbolAndSolutionOpt.Value;
var (symbol, project) = symbolAndProjectOpt.Value;
return await FindSourceImplementationsAsync(
solution, symbol, cancellationToken).ConfigureAwait(false);
project.Solution, symbol, cancellationToken).ConfigureAwait(false);
}
private static async Task<(Solution solution, ISymbol symbol, ImmutableArray<ISymbol> implementations, string message)?> FindSourceImplementationsAsync(
......
// 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;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.CodeAnalysis.Remote;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Editor.FindUsages
{
internal interface IRemoteFindUsagesService
{
Task FindReferencesAsync(
PinnedSolutionInfo solutionInfo,
SerializableSymbolAndProjectId symbolAndProjectIdArg,
SerializableFindReferencesSearchOptions options,
CancellationToken cancellationToken);
}
internal class FindReferencesServerCallback
{
private readonly Solution _solution;
private readonly IFindUsagesContext _context;
private readonly Dictionary<int, DefinitionItem> _idToDefinition = new Dictionary<int, DefinitionItem>();
public FindReferencesServerCallback(Solution solution, IFindUsagesContext context)
{
_solution = solution;
_context = context;
}
public Task AddItemsAsync(int count)
=> _context.ProgressTracker.AddItemsAsync(count);
public Task ItemCompletedAsync()
=> _context.ProgressTracker.ItemCompletedAsync();
public Task ReportMessageAsync(string message)
=> _context.ReportMessageAsync(message);
[Obsolete]
public Task ReportProgressAsync(int current, int maximum)
=> _context.ReportProgressAsync(current, maximum);
public Task SetSearchTitleAsync(string title)
=> _context.SetSearchTitleAsync(title);
public Task OnDefinitionFoundAsync(SerializableDefinitionItem definition)
{
var id = definition.Id;
var rehydrated = definition.Rehydrate(_solution);
lock (_idToDefinition)
{
_idToDefinition.Add(id, rehydrated);
}
return _context.OnDefinitionFoundAsync(rehydrated);
}
public Task OnReferenceFoundAsync(SerializableSourceReferenceItem reference)
=> _context.OnReferenceFoundAsync(reference.Rehydrate(_solution, GetDefinition(reference.DefinitionId)));
private DefinitionItem GetDefinition(int definitionId)
{
lock (_idToDefinition)
{
Contract.ThrowIfFalse(_idToDefinition.ContainsKey(definitionId));
return _idToDefinition[definitionId];
}
}
}
internal class SerializableDocumentSpan
{
public DocumentId DocumentId;
public TextSpan SourceSpan;
public static SerializableDocumentSpan Dehydrate(DocumentSpan documentSpan)
=> new SerializableDocumentSpan
{
DocumentId = documentSpan.Document.Id,
SourceSpan = documentSpan.SourceSpan,
};
public DocumentSpan Rehydrate(Solution solution)
=> new DocumentSpan(solution.GetDocument(DocumentId), SourceSpan);
}
internal class SerializableTaggedText
{
public string Tag;
public string Text;
public TaggedTextStyle Style;
public string NavigationTarget;
public string NavigationHint;
public static SerializableTaggedText Dehydrate(TaggedText text)
=> new SerializableTaggedText
{
Tag = text.Tag,
Text = text.Text,
Style = text.Style,
NavigationTarget = text.NavigationTarget,
NavigationHint = text.NavigationHint,
};
public TaggedText Rehydrate()
=> new TaggedText(Tag, Text, Style, NavigationTarget, NavigationHint);
}
internal class SerializableDefinitionItem
{
public int Id;
public string[] Tags;
public SerializableTaggedText[] DisplayParts;
public SerializableTaggedText[] NameDisplayParts;
public SerializableTaggedText[] OriginationParts;
public SerializableDocumentSpan[] SourceSpans;
public (string key, string value)[] Properties;
public (string key, string value)[] DisplayableProperties;
public bool DisplayIfNoReferences;
public static SerializableDefinitionItem Dehydrate(int id, DefinitionItem item)
=> new SerializableDefinitionItem
{
Id = id,
Tags = item.Tags.ToArray(),
DisplayParts = item.DisplayParts.Select(p => SerializableTaggedText.Dehydrate(p)).ToArray(),
NameDisplayParts = item.NameDisplayParts.Select(p => SerializableTaggedText.Dehydrate(p)).ToArray(),
OriginationParts = item.OriginationParts.Select(p => SerializableTaggedText.Dehydrate(p)).ToArray(),
SourceSpans = item.SourceSpans.Select(ss => SerializableDocumentSpan.Dehydrate(ss)).ToArray(),
Properties = item.Properties.Select(kvp => (kvp.Key, kvp.Value)).ToArray(),
DisplayableProperties = item.DisplayableProperties.Select(kvp => (kvp.Key, kvp.Value)).ToArray(),
DisplayIfNoReferences = item.DisplayIfNoReferences,
};
public DefinitionItem Rehydrate(Solution solution)
=> new DefinitionItem.DefaultDefinitionItem(
Tags.ToImmutableArray(),
DisplayParts.SelectAsArray(dp => dp.Rehydrate()),
NameDisplayParts.SelectAsArray(dp => dp.Rehydrate()),
OriginationParts.SelectAsArray(dp => dp.Rehydrate()),
SourceSpans.SelectAsArray(ss => ss.Rehydrate(solution)),
Properties.ToImmutableDictionary(t => t.key, t => t.value),
DisplayableProperties.ToImmutableDictionary(t => t.key, t => t.value),
DisplayIfNoReferences);
}
internal class SerializableSourceReferenceItem
{
public int DefinitionId;
public SerializableDocumentSpan SourceSpan;
public SymbolUsageInfo SymbolUsageInfo;
public (string Key, string Value)[] AdditionalProperties;
public static SerializableSourceReferenceItem Dehydrate(
int definitionId, SourceReferenceItem item)
{
return new SerializableSourceReferenceItem
{
DefinitionId = definitionId,
SourceSpan = SerializableDocumentSpan.Dehydrate(item.SourceSpan),
SymbolUsageInfo = item.SymbolUsageInfo,
AdditionalProperties = item.AdditionalProperties.Select(kvp => (kvp.Key, kvp.Value)).ToArray(),
};
}
public SourceReferenceItem Rehydrate(Solution solution, DefinitionItem definition)
{
return new SourceReferenceItem(
definition,
SourceSpan.Rehydrate(solution),
SymbolUsageInfo,
AdditionalProperties.ToImmutableDictionary(t => t.Key, t => t.Value));
}
}
}
......@@ -16,20 +16,20 @@ internal abstract partial class AbstractGoToBaseService : IGoToBaseService
public async Task FindBasesAsync(Document document, int position, IFindUsagesContext context)
{
var cancellationToken = context.CancellationToken;
var symbolAndSolutionOpt = await FindUsagesHelpers.GetRelevantSymbolAndSolutionAtPositionAsync(
var symbolAndProjectOpt = await FindUsagesHelpers.GetRelevantSymbolAndProjectAtPositionAsync(
document, position, cancellationToken).ConfigureAwait(false);
if (symbolAndSolutionOpt == null)
if (symbolAndProjectOpt == null)
{
await context.ReportMessageAsync(
EditorFeaturesResources.Cannot_navigate_to_the_symbol_under_the_caret).ConfigureAwait(false);
return;
}
var (symbol, solution) = symbolAndSolutionOpt.Value;
var (symbol, project) = symbolAndProjectOpt.Value;
var bases = FindBaseHelpers.FindBases(
symbol, solution, cancellationToken);
var solution = project.Solution;
var bases = FindBaseHelpers.FindBases(symbol, solution, cancellationToken);
await context.SetSearchTitleAsync(
string.Format(EditorFeaturesResources._0_bases,
......
......@@ -152,6 +152,7 @@ internal abstract partial class DefinitionItem
Contract.ThrowIfFalse(Properties.ContainsKey(MetadataSymbolOriginatingProjectIdDebugName));
}
}
public abstract bool CanNavigateTo(Workspace workspace);
public abstract bool TryNavigateTo(Workspace workspace, NavigationBehavior navigationBehavior);
......
......@@ -528,7 +528,7 @@ protected override bool TryExec(Guid commandGroup, uint commandId)
// thread.
await Task.Run(async () =>
{
await FindReferencesAsync(_threadingContext, symbolListItem, project, context, cancellationToken).ConfigureAwait(false);
await FindReferencesAsync(_threadingContext, symbolListItem, project, context).ConfigureAwait(false);
}, cancellationToken).ConfigureAwait(false);
// Note: we don't need to put this in a finally. The only time we might not hit
......@@ -546,15 +546,12 @@ protected override bool TryExec(Guid commandGroup, uint commandId)
}
}
private static async Task FindReferencesAsync(IThreadingContext threadingContext, SymbolListItem symbolListItem, Project project, CodeAnalysis.FindUsages.FindUsagesContext context, CancellationToken cancellationToken)
private static async Task FindReferencesAsync(IThreadingContext threadingContext, SymbolListItem symbolListItem, Project project, CodeAnalysis.FindUsages.FindUsagesContext context)
{
var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
var compilation = await project.GetCompilationAsync(context.CancellationToken).ConfigureAwait(false);
var symbol = symbolListItem.ResolveSymbol(compilation);
if (symbol != null)
{
await AbstractFindUsagesService.FindSymbolReferencesAsync(
threadingContext, context, symbol, project.Solution, cancellationToken).ConfigureAwait(false);
}
await AbstractFindUsagesService.FindSymbolReferencesAsync(context, symbol, project).ConfigureAwait(false);
}
}
}
// 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;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor.FindUsages;
using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Remote
{
// root level service for all Roslyn services
internal partial class CodeAnalysisService : IRemoteFindUsagesService
{
public Task FindReferencesAsync(PinnedSolutionInfo solutionInfo,
SerializableSymbolAndProjectId symbolAndProjectIdArg,
SerializableFindReferencesSearchOptions options,
CancellationToken cancellationToken)
{
return RunServiceAsync(async () =>
{
using (UserOperationBooster.Boost())
{
var solution = await GetSolutionAsync(solutionInfo, cancellationToken).ConfigureAwait(false);
var symbol = await symbolAndProjectIdArg.TryRehydrateAsync(
solution, cancellationToken).ConfigureAwait(false);
var project = solution.GetProject(symbolAndProjectIdArg.ProjectId);
var context = new RemoteFindUsageContext(solution, EndPoint, cancellationToken);
if (symbol == null)
return;
await AbstractFindUsagesService.FindReferencesAsync(
context, symbol, project, options.Rehydrate()).ConfigureAwait(false);
}
}, cancellationToken);
}
private class RemoteFindUsageContext : IFindUsagesContext, IStreamingProgressTracker
{
private readonly Solution _solution;
private readonly RemoteEndPoint _endPoint;
private readonly Dictionary<DefinitionItem, int> _definitionItemToId = new Dictionary<DefinitionItem, int>();
public CancellationToken CancellationToken { get; }
public RemoteFindUsageContext(Solution solution, RemoteEndPoint endPoint, CancellationToken cancellationToken)
{
_solution = solution;
_endPoint = endPoint;
CancellationToken = cancellationToken;
}
#region IStreamingProgressTracker
public Task AddItemsAsync(int count)
=> _endPoint.InvokeAsync(nameof(AddItemsAsync), new object[] { count }, CancellationToken);
public Task ItemCompletedAsync()
=> _endPoint.InvokeAsync(nameof(ItemCompletedAsync), Array.Empty<object>(), CancellationToken);
#endregion
#region IFindUsagesContext
public IStreamingProgressTracker ProgressTracker => this;
public Task ReportMessageAsync(string message)
=> _endPoint.InvokeAsync(nameof(ReportMessageAsync), new object[] { message }, CancellationToken);
public Task ReportProgressAsync(int current, int maximum)
=> _endPoint.InvokeAsync(nameof(ReportProgressAsync), new object[] { current, maximum }, CancellationToken);
public Task SetSearchTitleAsync(string title)
=> _endPoint.InvokeAsync(nameof(SetSearchTitleAsync), new object[] { title }, CancellationToken);
public Task OnExternalReferenceFoundAsync(ExternalReferenceItem reference)
=> throw ExceptionUtilities.Unreachable;
public Task OnDefinitionFoundAsync(DefinitionItem definition)
{
var id = GetOrAddDefinitionItemId(definition);
return _endPoint.InvokeAsync(nameof(OnDefinitionFoundAsync),
new object[]
{
SerializableDefinitionItem.Dehydrate(id, definition),
},
CancellationToken);
}
private int GetOrAddDefinitionItemId(DefinitionItem item)
{
lock (_definitionItemToId)
{
if (!_definitionItemToId.TryGetValue(item, out var id))
{
id = _definitionItemToId.Count;
_definitionItemToId.Add(item, id);
}
return id;
}
}
public Task OnReferenceFoundAsync(SourceReferenceItem reference)
{
var definitionItem = GetOrAddDefinitionItemId(reference.Definition);
return _endPoint.InvokeAsync(nameof(OnReferenceFoundAsync),
new object[]
{
SerializableSourceReferenceItem.Dehydrate(definitionItem, reference),
},
CancellationToken);
}
#endregion
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册