提交 680ad8ac 编写于 作者: C CyrusNajmabadi 提交者: GitHub

Merge pull request #15338 from CyrusNajmabadi/findUsages2

Present go-to-implementation results in the new Streaming FindRefs window if there are multiple results.
......@@ -14,8 +14,9 @@ internal class CSharpGoToDefinitionService : AbstractGoToDefinitionService
{
[ImportingConstructor]
public CSharpGoToDefinitionService(
[ImportMany]IEnumerable<Lazy<INavigableItemsPresenter>> presenters)
: base(presenters)
[ImportMany]IEnumerable<Lazy<INavigableItemsPresenter>> presenters,
[ImportMany]IEnumerable<Lazy<IStreamingFindUsagesPresenter>> streamingPresenters)
: base(presenters, streamingPresenters)
{
}
......
......@@ -14,8 +14,9 @@ internal sealed class CSharpGoToImplementationService : AbstractGoToImplementati
{
[ImportingConstructor]
public CSharpGoToImplementationService(
[ImportMany]IEnumerable<Lazy<INavigableItemsPresenter>> presenters)
: base(presenters)
[ImportMany]IEnumerable<Lazy<INavigableItemsPresenter>> presenters,
[ImportMany]IEnumerable<Lazy<IStreamingFindUsagesPresenter>> streamingPresenters)
: base(presenters, streamingPresenters)
{
}
}
......
......@@ -17,13 +17,16 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.GoToDefinition
internal abstract class AbstractGoToDefinitionService : IGoToDefinitionService
{
private readonly IEnumerable<Lazy<INavigableItemsPresenter>> _presenters;
private readonly IEnumerable<Lazy<IStreamingFindUsagesPresenter>> _streamingPresenters;
protected abstract ISymbol FindRelatedExplicitlyDeclaredSymbol(ISymbol symbol, Compilation compilation);
protected AbstractGoToDefinitionService(
IEnumerable<Lazy<INavigableItemsPresenter>> presenters)
IEnumerable<Lazy<INavigableItemsPresenter>> presenters,
IEnumerable<Lazy<IStreamingFindUsagesPresenter>> streamingPresenters)
{
_presenters = presenters;
_streamingPresenters = streamingPresenters;
}
private async Task<ISymbol> FindSymbolAsync(Document document, int position, CancellationToken cancellationToken)
......@@ -78,6 +81,7 @@ public bool TryGoToDefinition(Document document, int position, CancellationToken
return GoToDefinitionHelpers.TryGoToDefinition(symbol,
document.Project,
_presenters,
_streamingPresenters,
thirdPartyNavigationAllowed: isThirdPartyNavigationAllowed,
throwOnHiddenDefinition: true,
cancellationToken: cancellationToken);
......
......@@ -12,6 +12,7 @@
using Microsoft.CodeAnalysis.Navigation;
using Microsoft.CodeAnalysis.Options;
using Roslyn.Utilities;
using Microsoft.CodeAnalysis.FindUsages;
namespace Microsoft.CodeAnalysis.Editor.Implementation.GoToDefinition
{
......@@ -21,6 +22,7 @@ internal static class GoToDefinitionHelpers
ISymbol symbol,
Project project,
IEnumerable<Lazy<INavigableItemsPresenter>> presenters,
IEnumerable<Lazy<IStreamingFindUsagesPresenter>> streamingPresenters,
CancellationToken cancellationToken,
bool thirdPartyNavigationAllowed = true,
bool throwOnHiddenDefinition = false)
......@@ -68,7 +70,7 @@ internal static class GoToDefinitionHelpers
var displayParts = NavigableItemFactory.GetSymbolDisplayTaggedParts(project, symbol);
var title = displayParts.JoinText();
if (!preferredSourceLocations.Any())
if (preferredSourceLocations.Length == 0)
{
// If there are no visible source locations, then tell the host about the symbol and
// allow it to navigate to it. This will either navigate to any non-visible source
......@@ -81,64 +83,108 @@ internal static class GoToDefinitionHelpers
options: options.WithChangedOption(NavigationOptions.PreferProvisionalTab, true),
cancellationToken: cancellationToken);
}
else if (preferredSourceLocations.Length == 1)
{
var item = NavigableItemFactory.GetItemFromSymbolLocation(
solution, symbol, preferredSourceLocations[0],
displayTaggedParts: null);
return TryGoToSingleLocation(item, options, throwOnHiddenDefinition);
}
else
{
// We have multiple viable source locations, so ask the host what to do. Most hosts
// will simply display the results to the user and allow them to choose where to
// go.
var navigableItems = preferredSourceLocations.Select(location =>
NavigableItemFactory.GetItemFromSymbolLocation(
solution, symbol, location,
displayTaggedParts: null)).ToImmutableArray();
return TryGoToDefinition(navigableItems, title, options, presenters, throwOnHiddenDefinition);
return TryPresentInFindUsagesPresenter(solution, symbol, streamingPresenters, cancellationToken) ||
TryPresentInNavigableItemsPresenter(solution, symbol, presenters, title, preferredSourceLocations);
}
}
private static bool TryGoToDefinition(
ImmutableArray<INavigableItem> navigableItems,
string title,
OptionSet options,
IEnumerable<Lazy<INavigableItemsPresenter>> presenters,
bool throwOnHiddenDefinition)
private static bool TryPresentInFindUsagesPresenter(
Solution solution, ISymbol symbol,
IEnumerable<Lazy<IStreamingFindUsagesPresenter>> streamingPresenters,
CancellationToken cancellationToken)
{
Contract.ThrowIfNull(options);
var presenter = GetFindUsagesPresenter(streamingPresenters);
if (presenter == null)
{
return false;
}
// If we have a single location, then just navigate to it.
if (navigableItems.Length == 1 && navigableItems[0].Document != null)
var definition = symbol.ToDefinitionItem(solution);
var context = presenter.StartSearch(EditorFeaturesResources.Go_to_Definition);
try
{
var firstItem = navigableItems[0];
var workspace = firstItem.Document.Project.Solution.Workspace;
var navigationService = workspace.Services.GetService<IDocumentNavigationService>();
context.OnDefinitionFoundAsync(definition).Wait(cancellationToken);
}
finally
{
context.OnCompletedAsync().Wait(cancellationToken);
}
if (navigationService.CanNavigateToSpan(workspace, firstItem.Document.Id, firstItem.SourceSpan))
{
return navigationService.TryNavigateToSpan(
workspace,
documentId: firstItem.Document.Id,
textSpan: firstItem.SourceSpan,
options: options.WithChangedOption(NavigationOptions.PreferProvisionalTab, true));
}
else
{
if (throwOnHiddenDefinition)
{
const int E_FAIL = -2147467259;
throw new COMException(EditorFeaturesResources.The_definition_of_the_object_is_hidden, E_FAIL);
}
else
{
return false;
}
}
return true;
}
private static IStreamingFindUsagesPresenter GetFindUsagesPresenter(
IEnumerable<Lazy<IStreamingFindUsagesPresenter>> streamingPresenters)
{
try
{
return streamingPresenters.FirstOrDefault()?.Value;
}
else
catch
{
// We have multiple viable source locations, so ask the host what to do. Most hosts
// will simply display the results to the user and allow them to choose where to
// go.
return null;
}
}
private static bool TryPresentInNavigableItemsPresenter(
Solution solution, ISymbol symbol,
IEnumerable<Lazy<INavigableItemsPresenter>> presenters,
string title, Location[] locations)
{
var presenter = presenters.FirstOrDefault();
if (presenter != null)
{
var navigableItems = locations.Select(location =>
NavigableItemFactory.GetItemFromSymbolLocation(
solution, symbol, location,
displayTaggedParts: null)).ToImmutableArray();
presenter.Value.DisplayResult(title, navigableItems);
return true;
}
return false;
}
if (presenters.Any())
private static bool TryGoToSingleLocation(
INavigableItem navigableItem, OptionSet options, bool throwOnHiddenDefinition)
{
var firstItem = navigableItem;
var workspace = firstItem.Document.Project.Solution.Workspace;
var navigationService = workspace.Services.GetService<IDocumentNavigationService>();
if (navigationService.CanNavigateToSpan(workspace, firstItem.Document.Id, firstItem.SourceSpan))
{
return navigationService.TryNavigateToSpan(
workspace,
documentId: firstItem.Document.Id,
textSpan: firstItem.SourceSpan,
options: options.WithChangedOption(NavigationOptions.PreferProvisionalTab, true));
}
else
{
if (throwOnHiddenDefinition)
{
presenters.First().Value.DisplayResult(title, navigableItems);
return true;
const int E_FAIL = -2147467259;
throw new COMException(EditorFeaturesResources.The_definition_of_the_object_is_hidden, E_FAIL);
}
else
{
return false;
}
return false;
}
}
......
......@@ -7,6 +7,7 @@
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Editor.SymbolMapping;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Navigation;
using Microsoft.CodeAnalysis.Shared.Extensions;
......@@ -17,11 +18,14 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.GoToImplementation
internal abstract class AbstractGoToImplementationService : IGoToImplementationService
{
private readonly IEnumerable<Lazy<INavigableItemsPresenter>> _navigableItemPresenters;
private readonly IEnumerable<Lazy<IStreamingFindUsagesPresenter>> _streamingPresenters;
public AbstractGoToImplementationService(
IEnumerable<Lazy<INavigableItemsPresenter>> navigableItemPresenters)
IEnumerable<Lazy<INavigableItemsPresenter>> navigableItemPresenters,
IEnumerable<Lazy<IStreamingFindUsagesPresenter>> streamingPresenters)
{
_navigableItemPresenters = navigableItemPresenters;
_streamingPresenters = streamingPresenters;
}
public bool TryGoToImplementation(Document document, int position, CancellationToken cancellationToken, out string message)
......@@ -93,16 +97,16 @@ private bool TryGoToImplementationOnMappedSymbol(SymbolMappingResult mapping, Ca
else
{
// This is something boring like a regular method or type, so we'll just go there directly
if (GoToDefinition.GoToDefinitionHelpers.TryGoToDefinition(mapping.Symbol, mapping.Project, _navigableItemPresenters, cancellationToken))
if (GoToDefinition.GoToDefinitionHelpers.TryGoToDefinition(
mapping.Symbol, mapping.Project,
_navigableItemPresenters, _streamingPresenters, cancellationToken))
{
message = null;
return true;
}
else
{
message = EditorFeaturesResources.Cannot_navigate_to_the_symbol_under_the_caret;
return false;
}
message = EditorFeaturesResources.Cannot_navigate_to_the_symbol_under_the_caret;
return false;
}
}
......@@ -119,24 +123,75 @@ private bool TryGoToImplementations(IEnumerable<ISymbol> candidateImplementation
}
else if (implementations.Count == 1)
{
GoToDefinition.GoToDefinitionHelpers.TryGoToDefinition(implementations.Single(), mapping.Project, _navigableItemPresenters, cancellationToken);
GoToDefinition.GoToDefinitionHelpers.TryGoToDefinition(
implementations.Single(), mapping.Project,
_navigableItemPresenters, _streamingPresenters, cancellationToken);
message = null;
return true;
}
else
{
// We have multiple symbols, so we'll build a list of all preferred locations for all the symbols
var navigableItems = implementations.SelectMany(
implementation => CreateItemsForImplementation(implementation, mapping.Solution));
return TryPresentInFindUsagesPresenter(mapping, implementations, cancellationToken, out message) ||
TryPresentInNavigableItemsPresenter(mapping, implementations, out message);
}
}
private bool TryPresentInFindUsagesPresenter(
SymbolMappingResult mapping, List<ISymbol> implementations, CancellationToken cancellationToken, out string message)
{
message = null;
var presenter = _navigableItemPresenters.First();
var presenter = GetFindUsagesPresenter();
if (presenter == null)
{
return false;
}
var taggedParts = NavigableItemFactory.GetSymbolDisplayTaggedParts(mapping.Project, mapping.Symbol);
var definitionItems = implementations.Select(s =>
s.ToDefinitionItem(mapping.Solution)).ToImmutableArrayOrEmpty();
presenter.Value.DisplayResult(taggedParts.JoinText(), navigableItems);
message = null;
return true;
var context = presenter.StartSearch(EditorFeaturesResources.Go_To_Implementation);
try
{
foreach (var item in definitionItems)
{
context.OnDefinitionFoundAsync(item).Wait(cancellationToken);
}
}
finally
{
context.OnCompletedAsync().Wait(cancellationToken);
}
return true;
}
private IStreamingFindUsagesPresenter GetFindUsagesPresenter()
{
try
{
return _streamingPresenters.FirstOrDefault()?.Value;
}
catch
{
return null;
}
}
private bool TryPresentInNavigableItemsPresenter(
SymbolMappingResult mapping, List<ISymbol> implementations, out string message)
{
// We have multiple symbols, so we'll build a list of all preferred locations for all the symbols
var navigableItems = implementations.SelectMany(
implementation => CreateItemsForImplementation(implementation, mapping.Solution));
var presenter = _navigableItemPresenters.First();
var taggedParts = NavigableItemFactory.GetSymbolDisplayTaggedParts(mapping.Project, mapping.Symbol);
presenter.Value.DisplayResult(taggedParts.JoinText(), navigableItems);
message = null;
return true;
}
private static IEnumerable<INavigableItem> CreateItemsForImplementation(ISymbol implementation, Solution solution)
......
......@@ -100,7 +100,7 @@ class C
Dim cursorBuffer = cursorDocument.TextBuffer
Dim document = workspace.CurrentSolution.GetDocument(cursorDocument.Id)
Dim goToDefService = New CSharpGoToDefinitionService(presenters)
Dim goToDefService = New CSharpGoToDefinitionService(presenters, {})
Dim waitContext = New TestWaitContext(updatesBeforeCancel)
Dim waitIndicator = New TestWaitIndicator(waitContext)
......
......@@ -13,8 +13,8 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.GoToDefinition
Return GoToTestHelpers.TestAsync(workspaceDefinition, expectedResult,
Function(document As Document, cursorPosition As Integer, presenters As IEnumerable(Of Lazy(Of INavigableItemsPresenter)))
Dim goToDefService = If(document.Project.Language = LanguageNames.CSharp,
DirectCast(New CSharpGoToDefinitionService(presenters), IGoToDefinitionService),
New VisualBasicGoToDefinitionService(presenters))
DirectCast(New CSharpGoToDefinitionService(presenters, {}), IGoToDefinitionService),
New VisualBasicGoToDefinitionService(presenters, {}))
Return goToDefService.TryGoToDefinition(document, cursorPosition, CancellationToken.None)
End Function)
......
......@@ -13,8 +13,8 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.GoToImplementation
Return GoToTestHelpers.TestAsync(workspaceDefinition, shouldSucceed,
Function(document As Document, cursorPosition As Integer, presenters As IEnumerable(Of Lazy(Of INavigableItemsPresenter)))
Dim service = If(document.Project.Language = LanguageNames.CSharp,
DirectCast(New CSharpGoToImplementationService(presenters), IGoToImplementationService),
New VisualBasicGoToImplementationService(presenters))
DirectCast(New CSharpGoToImplementationService(presenters, {}), IGoToImplementationService),
New VisualBasicGoToImplementationService(presenters, {}))
Dim message As String = Nothing
Return service.TryGoToImplementation(document, cursorPosition, CancellationToken.None, message)
......
......@@ -12,8 +12,9 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.GoToDefinition
Inherits AbstractGoToDefinitionService
<ImportingConstructor>
Public Sub New(<ImportMany> presenters As IEnumerable(Of Lazy(Of INavigableItemsPresenter)))
MyBase.New(presenters)
Public Sub New(<ImportMany> presenters As IEnumerable(Of Lazy(Of INavigableItemsPresenter)),
<ImportMany> streamingPresenters As IEnumerable(Of Lazy(Of IStreamingFindUsagesPresenter)))
MyBase.New(presenters, streamingPresenters)
End Sub
Protected Overrides Function FindRelatedExplicitlyDeclaredSymbol(symbol As ISymbol, compilation As Compilation) As ISymbol
......
......@@ -11,8 +11,9 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.GoToImplementation
Inherits AbstractGoToImplementationService
<ImportingConstructor>
Public Sub New(<ImportMany> presenters As IEnumerable(Of Lazy(Of INavigableItemsPresenter)))
MyBase.New(presenters)
Public Sub New(<ImportMany> presenters As IEnumerable(Of Lazy(Of INavigableItemsPresenter)),
<ImportMany> streamingPresenters As IEnumerable(Of Lazy(Of IStreamingFindUsagesPresenter)))
MyBase.New(presenters, streamingPresenters)
End Sub
End Class
End Namespace
......@@ -28,6 +28,7 @@ namespace Microsoft.VisualStudio.LanguageServices
internal class RoslynVisualStudioWorkspace : VisualStudioWorkspaceImpl
{
private readonly IEnumerable<Lazy<INavigableItemsPresenter>> _navigableItemsPresenters;
private readonly IEnumerable<Lazy<IStreamingFindUsagesPresenter>> _streamingPresenters;
private readonly IEnumerable<Lazy<IDefinitionsAndReferencesPresenter>> _referencedSymbolsPresenters;
[ImportingConstructor]
......@@ -35,6 +36,7 @@ internal class RoslynVisualStudioWorkspace : VisualStudioWorkspaceImpl
SVsServiceProvider serviceProvider,
SaveEventsService saveEventsService,
[ImportMany] IEnumerable<Lazy<INavigableItemsPresenter>> navigableItemsPresenters,
[ImportMany] IEnumerable<Lazy<IStreamingFindUsagesPresenter>> streamingPresenters,
[ImportMany] IEnumerable<Lazy<IDefinitionsAndReferencesPresenter>> referencedSymbolsPresenters,
[ImportMany] IEnumerable<IDocumentOptionsProviderFactory> documentOptionsProviderFactories)
: base(
......@@ -46,6 +48,7 @@ internal class RoslynVisualStudioWorkspace : VisualStudioWorkspaceImpl
InitializeStandardVisualStudioWorkspace(serviceProvider, saveEventsService);
_navigableItemsPresenters = navigableItemsPresenters;
_streamingPresenters = streamingPresenters;
_referencedSymbolsPresenters = referencedSymbolsPresenters;
foreach (var providerFactory in documentOptionsProviderFactories)
......@@ -179,9 +182,11 @@ private static bool TryResolveSymbol(ISymbol symbol, Project project, Cancellati
return true;
}
public override bool TryGoToDefinition(ISymbol symbol, Project project, CancellationToken cancellationToken)
public override bool TryGoToDefinition(
ISymbol symbol, Project project, CancellationToken cancellationToken)
{
if (!_navigableItemsPresenters.Any())
if (!_navigableItemsPresenters.Any() &&
!_streamingPresenters.Any())
{
return false;
}
......@@ -193,7 +198,8 @@ public override bool TryGoToDefinition(ISymbol symbol, Project project, Cancella
}
return GoToDefinitionHelpers.TryGoToDefinition(
searchSymbol, searchProject, _navigableItemsPresenters, cancellationToken: cancellationToken);
searchSymbol, searchProject,
_navigableItemsPresenters, _streamingPresenters, cancellationToken);
}
public override bool TryFindAllReferences(ISymbol symbol, Project project, CancellationToken cancellationToken)
......
......@@ -39,7 +39,9 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.GoToDefinition
WpfTestCase.RequireWpfFact($"{NameOf(GoToDefinitionHelpers)}.{NameOf(GoToDefinitionHelpers.TryGoToDefinition)} assumes it's on the UI thread with a WaitAndGetResult call")
Dim success = GoToDefinitionHelpers.TryGoToDefinition(
symbolInfo.Symbol, document.Project, {New Lazy(Of INavigableItemsPresenter)(Function() presenter)}, thirdPartyNavigationAllowed:=True, throwOnHiddenDefinition:=False, cancellationToken:=CancellationToken.None)
symbolInfo.Symbol, document.Project,
{New Lazy(Of INavigableItemsPresenter)(Function() presenter)}, {},
thirdPartyNavigationAllowed:=True, throwOnHiddenDefinition:=False, cancellationToken:=CancellationToken.None)
Assert.Equal(expectSuccess, success)
End Using
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册