提交 b8b09cce 编写于 作者: C CyrusNajmabadi

Make the GoToDefinitionHelper sit on top of the new FindUsages presenter.

上级 1d1a5c96
......@@ -14,9 +14,8 @@ internal class CSharpGoToDefinitionService : AbstractGoToDefinitionService
{
[ImportingConstructor]
public CSharpGoToDefinitionService(
[ImportMany]IEnumerable<Lazy<INavigableItemsPresenter>> presenters,
[ImportMany]IEnumerable<Lazy<IStreamingFindUsagesPresenter>> streamingPresenters)
: base(presenters, streamingPresenters)
: base(streamingPresenters)
{
}
......
......@@ -16,16 +16,13 @@ namespace Microsoft.CodeAnalysis.Editor.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<IStreamingFindUsagesPresenter>> streamingPresenters)
{
_presenters = presenters;
_streamingPresenters = streamingPresenters;
}
......@@ -80,7 +77,6 @@ public bool TryGoToDefinition(Document document, int position, CancellationToken
return GoToDefinitionHelpers.TryGoToDefinition(symbol,
document.Project,
_presenters,
_streamingPresenters,
thirdPartyNavigationAllowed: isThirdPartyNavigationAllowed,
throwOnHiddenDefinition: true,
......
......@@ -2,17 +2,15 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.CodeAnalysis.Navigation;
using Microsoft.CodeAnalysis.Options;
using Roslyn.Utilities;
using Microsoft.CodeAnalysis.FindUsages;
namespace Microsoft.CodeAnalysis.Editor.GoToDefinition
{
......@@ -21,7 +19,6 @@ internal static class GoToDefinitionHelpers
public static bool TryGoToDefinition(
ISymbol symbol,
Project project,
IEnumerable<Lazy<INavigableItemsPresenter>> presenters,
IEnumerable<Lazy<IStreamingFindUsagesPresenter>> streamingPresenters,
CancellationToken cancellationToken,
bool thirdPartyNavigationAllowed = true,
......@@ -52,9 +49,12 @@ internal static class GoToDefinitionHelpers
symbol = definition ?? symbol;
if (thirdPartyNavigationAllowed && TryThirdPartyNavigation(symbol, solution))
var definitions = ArrayBuilder<DefinitionItem>.GetInstance();
if (thirdPartyNavigationAllowed )
{
return true;
var factory = solution.Workspace.Services.GetService<IDefinitionsAndReferencesFactory>();
var thirdPartyItem = factory.GetThirdPartyDefinitionItem(solution, symbol);
definitions.AddIfNotNull(thirdPartyItem);
}
// If it is a partial method declaration with no body, choose to go to the implementation
......@@ -66,64 +66,14 @@ internal static class GoToDefinitionHelpers
var options = project.Solution.Options;
var preferredSourceLocations = NavigableItemFactory.GetPreferredSourceLocations(solution, symbol).ToArray();
var displayParts = NavigableItemFactory.GetSymbolDisplayTaggedParts(project, symbol);
var title = displayParts.JoinText();
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
// locations, or it can appropriately deal with metadata symbols for hosts that can go
// to a metadata-as-source view.
definitions.Add(symbol.ToDefinitionItem(solution));
var symbolNavigationService = solution.Workspace.Services.GetService<ISymbolNavigationService>();
return symbolNavigationService.TryNavigateToSymbol(
symbol, project,
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.
return TryPresentInFindUsagesPresenter(solution, symbol, streamingPresenters, cancellationToken) ||
TryPresentInNavigableItemsPresenter(solution, symbol, presenters, title, preferredSourceLocations);
}
}
private static bool TryPresentInFindUsagesPresenter(
Solution solution, ISymbol symbol,
IEnumerable<Lazy<IStreamingFindUsagesPresenter>> streamingPresenters,
CancellationToken cancellationToken)
{
var presenter = GetFindUsagesPresenter(streamingPresenters);
if (presenter == null)
{
return false;
}
var definition = symbol.ToDefinitionItem(solution);
var context = presenter.StartSearch(EditorFeaturesResources.Go_to_Definition);
try
{
context.OnDefinitionFoundAsync(definition).Wait(cancellationToken);
}
finally
{
context.OnCompletedAsync().Wait(cancellationToken);
}
var result = presenter.NavigateToOrPresentItemsAsync(
EditorFeaturesResources.Go_to_Definition,
definitions.ToImmutableAndFree()).WaitAndGetResult(cancellationToken);
return true;
return result;
}
private static IStreamingFindUsagesPresenter GetFindUsagesPresenter(
......@@ -139,55 +89,6 @@ internal static class GoToDefinitionHelpers
}
}
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;
}
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)
{
const int E_FAIL = -2147467259;
throw new COMException(EditorFeaturesResources.The_definition_of_the_object_is_hidden, E_FAIL);
}
else
{
return false;
}
}
}
private static bool TryThirdPartyNavigation(ISymbol symbol, Solution solution)
{
var symbolNavigationService = solution.Workspace.Services.GetService<ISymbolNavigationService>();
......
......@@ -30,7 +30,7 @@ internal static class IStreamingFindUsagesPresenterExtensions
/// If there's only a single item, navigates to it. Otherwise, presents all the
/// items to the user.
/// </summary>
public static async Task NavigateToOrPresentItemsAsync(
public static async Task<bool> NavigateToOrPresentItemsAsync(
this IStreamingFindUsagesPresenter presenter,
string title, ImmutableArray<DefinitionItem> items)
{
......@@ -44,14 +44,14 @@ internal static class IStreamingFindUsagesPresenterExtensions
{
if (item.TryNavigateTo())
{
return;
return true;
}
}
var nonExternalItems = definitions.WhereAsArray(d => !d.IsExternal);
if (nonExternalItems.Length == 0)
{
return;
return false;
}
if (nonExternalItems.Length == 1 &&
......@@ -59,7 +59,7 @@ internal static class IStreamingFindUsagesPresenterExtensions
{
// There was only one location to navigate to. Just directly go to that location.
nonExternalItems[0].TryNavigateTo();
return;
return true;
}
// We have multiple definitions, or we have definitions with multiple locations.
......@@ -77,6 +77,7 @@ internal static class IStreamingFindUsagesPresenterExtensions
// context it has completed. In the latter case somethign wrong has happened
// and we don't want to run any more code code in this particular context.
await context.OnCompletedAsync().ConfigureAwait(false);
return true;
}
}
}
\ No newline at end of file
......@@ -94,13 +94,13 @@ class C
Dim mockDocumentNavigationService = DirectCast(workspace.Services.GetService(Of IDocumentNavigationService)(), MockDocumentNavigationService)
Dim navigatedTo = False
Dim presenter = New MockNavigableItemsPresenter(Sub(i) navigatedTo = True)
Dim presenters = {New Lazy(Of INavigableItemsPresenter)(Function() presenter)}
Dim presenter = New MockStreamingFindUsagesPresenter(Sub() navigatedTo = True)
Dim presenters = {New Lazy(Of IStreamingFindUsagesPresenter)(Function() presenter)}
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)
......
......@@ -15,15 +15,13 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.GoToDefinition
Public Class GoToDefinitionTests
Friend Async Function TestAsync(workspaceDefinition As XElement,
expectedResult As Boolean,
executeOnDocument As Func(Of Document, Integer, IEnumerable(Of Lazy(Of INavigableItemsPresenter)), Boolean)) As System.Threading.Tasks.Task
executeOnDocument As Func(Of Document, Integer, IEnumerable(Of Lazy(Of IStreamingFindUsagesPresenter)), Boolean)) As System.Threading.Tasks.Task
Using workspace = Await TestWorkspace.CreateAsync(
workspaceDefinition, exportProvider:=GoToTestHelpers.ExportProvider)
Dim solution = workspace.CurrentSolution
Dim cursorDocument = workspace.Documents.First(Function(d) d.CursorPosition.HasValue)
Dim cursorPosition = cursorDocument.CursorPosition.Value
Dim items As IList(Of INavigableItem) = Nothing
' Set up mocks. The IDocumentNavigationService should be called if there is one,
' location and the INavigableItemsPresenter should be called if there are
' multiple locations.
......@@ -38,8 +36,9 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.GoToDefinition
Dim mockDocumentNavigationService = DirectCast(workspace.Services.GetService(Of IDocumentNavigationService)(), MockDocumentNavigationService)
Dim presenter = New MockNavigableItemsPresenter(Sub(i) items = i)
Dim presenters = {New Lazy(Of INavigableItemsPresenter)(Function() presenter)}
Dim presenterCalled As Boolean = False
Dim presenter = New MockStreamingFindUsagesPresenter(Sub() presenterCalled = True)
Dim presenters = {New Lazy(Of IStreamingFindUsagesPresenter)(Function() presenter)}
Dim actualResult = executeOnDocument(document, cursorPosition, presenters)
Assert.Equal(expectedResult, actualResult)
......@@ -54,6 +53,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.GoToDefinition
expectedLocations.Sort()
Dim context = presenter.Context
If expectedResult Then
If mockDocumentNavigationService._triedNavigationToSpan Then
Dim definitionDocument = workspace.GetTestDocument(mockDocumentNavigationService._documentId)
......@@ -61,16 +61,20 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.GoToDefinition
Assert.Equal(definitionDocument.SelectedSpans.Single(), mockDocumentNavigationService._span)
' The INavigableItemsPresenter should not have been called
Assert.Null(items)
Assert.False(presenterCalled)
Else
Assert.False(mockDocumentNavigationService._triedNavigationToPosition)
Assert.False(mockDocumentNavigationService._triedNavigationToLineAndOffset)
Assert.NotEmpty(items)
Assert.True(presenterCalled)
Dim actualLocations As New List(Of FilePathAndSpan)
Dim items = context.GetDefinitions()
For Each location In items
actualLocations.Add(New FilePathAndSpan(location.Document.FilePath, location.SourceSpan))
For Each docSpan In location.SourceSpans
actualLocations.Add(New FilePathAndSpan(docSpan.Document.FilePath, docSpan.SourceSpan))
Next
Next
actualLocations.Sort()
......@@ -81,17 +85,17 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.GoToDefinition
End If
Else
Assert.Null(mockDocumentNavigationService._documentId)
Assert.True(items Is Nothing OrElse items.Count = 0)
Assert.False(presenterCalled)
End If
End Using
End Function
Private Function TestAsync(workspaceDefinition As XElement, Optional expectedResult As Boolean = True) As Tasks.Task
Return TestAsync(workspaceDefinition, expectedResult,
Function(document As Document, cursorPosition As Integer, presenters As IEnumerable(Of Lazy(Of INavigableItemsPresenter)))
Function(document As Document, cursorPosition As Integer, presenters As IEnumerable(Of Lazy(Of IStreamingFindUsagesPresenter)))
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)
......
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Threading
Imports Microsoft.CodeAnalysis.Editor.FindUsages
Imports Microsoft.CodeAnalysis.Editor.Host
Imports Microsoft.CodeAnalysis.Navigation
Imports Microsoft.CodeAnalysis.FindUsages
Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Utilities.GoToHelpers
Friend Class MockNavigableItemsPresenter
Implements INavigableItemsPresenter
Friend Class MockStreamingFindUsagesPresenter
Implements IStreamingFindUsagesPresenter
Private ReadOnly _callback As Action(Of IList(Of INavigableItem))
Public ReadOnly Context As New SimpleFindUsagesContext(CancellationToken.None)
Private ReadOnly _action As Action
Public Sub New(callback As Action(Of IList(Of INavigableItem)))
_callback = callback
Public Sub New(action As Action)
_action = action
End Sub
Public Sub DisplayResult(title As String, items As IEnumerable(Of INavigableItem)) Implements INavigableItemsPresenter.DisplayResult
_callback(items.ToList())
End Sub
Public Function StartSearch(title As String) As FindUsagesContext Implements IStreamingFindUsagesPresenter.StartSearch
_action()
Return Context
End Function
End Class
End Namespace
End Namespace
\ No newline at end of file
......@@ -12,9 +12,8 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.GoToDefinition
Inherits AbstractGoToDefinitionService
<ImportingConstructor>
Public Sub New(<ImportMany> presenters As IEnumerable(Of Lazy(Of INavigableItemsPresenter)),
<ImportMany> streamingPresenters As IEnumerable(Of Lazy(Of IStreamingFindUsagesPresenter)))
MyBase.New(presenters, streamingPresenters)
Public Sub New(<ImportMany> streamingPresenters As IEnumerable(Of Lazy(Of IStreamingFindUsagesPresenter)))
MyBase.New(streamingPresenters)
End Sub
Protected Overrides Function FindRelatedExplicitlyDeclaredSymbol(symbol As ISymbol, compilation As Compilation) As ISymbol
......
......@@ -27,7 +27,6 @@ namespace Microsoft.VisualStudio.LanguageServices
[Export(typeof(VisualStudioWorkspaceImpl))]
internal class RoslynVisualStudioWorkspace : VisualStudioWorkspaceImpl
{
private readonly IEnumerable<Lazy<INavigableItemsPresenter>> _navigableItemsPresenters;
private readonly IEnumerable<Lazy<IStreamingFindUsagesPresenter>> _streamingPresenters;
private readonly IEnumerable<Lazy<IDefinitionsAndReferencesPresenter>> _referencedSymbolsPresenters;
......@@ -35,7 +34,6 @@ internal class RoslynVisualStudioWorkspace : VisualStudioWorkspaceImpl
private RoslynVisualStudioWorkspace(
SVsServiceProvider serviceProvider,
SaveEventsService saveEventsService,
[ImportMany] IEnumerable<Lazy<INavigableItemsPresenter>> navigableItemsPresenters,
[ImportMany] IEnumerable<Lazy<IStreamingFindUsagesPresenter>> streamingPresenters,
[ImportMany] IEnumerable<Lazy<IDefinitionsAndReferencesPresenter>> referencedSymbolsPresenters,
[ImportMany] IEnumerable<IDocumentOptionsProviderFactory> documentOptionsProviderFactories)
......@@ -47,7 +45,6 @@ internal class RoslynVisualStudioWorkspace : VisualStudioWorkspaceImpl
InitializeStandardVisualStudioWorkspace(serviceProvider, saveEventsService);
_navigableItemsPresenters = navigableItemsPresenters;
_streamingPresenters = streamingPresenters;
_referencedSymbolsPresenters = referencedSymbolsPresenters;
......@@ -185,8 +182,7 @@ private static bool TryResolveSymbol(ISymbol symbol, Project project, Cancellati
public override bool TryGoToDefinition(
ISymbol symbol, Project project, CancellationToken cancellationToken)
{
if (!_navigableItemsPresenters.Any() &&
!_streamingPresenters.Any())
if (!_streamingPresenters.Any())
{
return false;
}
......@@ -199,7 +195,7 @@ private static bool TryResolveSymbol(ISymbol symbol, Project project, Cancellati
return GoToDefinitionHelpers.TryGoToDefinition(
searchSymbol, searchProject,
_navigableItemsPresenters, _streamingPresenters, cancellationToken);
_streamingPresenters, cancellationToken);
}
public override bool TryFindAllReferences(ISymbol symbol, Project project, CancellationToken cancellationToken)
......
......@@ -34,13 +34,14 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.GoToDefinition
Assert.NotNull(symbolInfo.Symbol)
Dim presenter = New MockNavigableItemsPresenter(Sub() Exit Sub)
Dim presenter = New MockStreamingFindUsagesPresenter(Sub () Exit sub)
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)
{New Lazy(Of IStreamingFindUsagesPresenter)(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.
先完成此消息的编辑!
想要评论请 注册