提交 57a2bf36 编写于 作者: C CyrusNajmabadi

Start refactoring how goto-impl works.

上级 d4636265
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Composition; using System.Composition;
using Microsoft.CodeAnalysis.Editor.FindReferences;
using Microsoft.CodeAnalysis.Editor.Host; using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Editor.Implementation.FindReferences;
using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Host.Mef;
namespace Microsoft.CodeAnalysis.Editor.CSharp.FindReferences namespace Microsoft.CodeAnalysis.Editor.CSharp.FindReferences
......
...@@ -3,8 +3,8 @@ ...@@ -3,8 +3,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Composition; using System.Composition;
using Microsoft.CodeAnalysis.Editor.GoToImplementation;
using Microsoft.CodeAnalysis.Editor.Host; using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Editor.Implementation.GoToImplementation;
using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Host.Mef;
namespace Microsoft.CodeAnalysis.Editor.CSharp.GoToImplementation namespace Microsoft.CodeAnalysis.Editor.CSharp.GoToImplementation
...@@ -14,9 +14,19 @@ internal sealed class CSharpGoToImplementationService : AbstractGoToImplementati ...@@ -14,9 +14,19 @@ internal sealed class CSharpGoToImplementationService : AbstractGoToImplementati
{ {
[ImportingConstructor] [ImportingConstructor]
public CSharpGoToImplementationService( public CSharpGoToImplementationService(
[ImportMany]IEnumerable<Lazy<INavigableItemsPresenter>> presenters, [ImportMany]IEnumerable<Lazy<INavigableItemsPresenter>> presenters)
[ImportMany]IEnumerable<Lazy<IStreamingFindUsagesPresenter>> streamingPresenters) : base(presenters)
: base(presenters, streamingPresenters) {
}
}
[ExportLanguageService(typeof(IStreamingFindImplementationsService), LanguageNames.CSharp), Shared]
internal sealed class CSharpStreamingFindImplementationsService : AbstractGoToImplementationService
{
[ImportingConstructor]
public CSharpStreamingFindImplementationsService(
[ImportMany]IEnumerable<Lazy<INavigableItemsPresenter>> presenters)
: base(presenters)
{ {
} }
} }
......
...@@ -108,6 +108,7 @@ ...@@ -108,6 +108,7 @@
<InternalsVisibleToMoq Include="DynamicProxyGenAssembly2" /> <InternalsVisibleToMoq Include="DynamicProxyGenAssembly2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="FindUsages\FindUsagesHelpers.cs" />
<Compile Include="FindUsages\FindUsagesContext.cs" /> <Compile Include="FindUsages\FindUsagesContext.cs" />
<Compile Include="FindUsages\IFindUsagesContext.cs" /> <Compile Include="FindUsages\IFindUsagesContext.cs" />
<Compile Include="Implementation\NavigateTo\AbstractNavigateToItemDisplay.cs" /> <Compile Include="Implementation\NavigateTo\AbstractNavigateToItemDisplay.cs" />
...@@ -256,9 +257,9 @@ ...@@ -256,9 +257,9 @@
<Compile Include="Implementation\Diagnostics\DiagnosticsSuggestionTaggerProvider.cs" /> <Compile Include="Implementation\Diagnostics\DiagnosticsSuggestionTaggerProvider.cs" />
<Compile Include="Implementation\Diagnostics\SuggestionAdornmentManagerProvider.cs" /> <Compile Include="Implementation\Diagnostics\SuggestionAdornmentManagerProvider.cs" />
<Compile Include="Implementation\Diagnostics\SuggestionTag.cs" /> <Compile Include="Implementation\Diagnostics\SuggestionTag.cs" />
<Compile Include="Implementation\FindReferences\AbstractFindReferencesService.ProgressAdapter.cs" /> <Compile Include="FindReferences\AbstractFindReferencesService.ProgressAdapter.cs" />
<Compile Include="Implementation\GoToImplementation\AbstractGoToImplementationService.cs" /> <Compile Include="GoToImplementation\AbstractGoToImplementationService.cs" />
<Compile Include="Implementation\GoToImplementation\IGoToImplementationService.cs" /> <Compile Include="GoToImplementation\IGoToImplementationService.cs" />
<Compile Include="Implementation\Intellisense\Completion\BraceCompletionMetadata.cs" /> <Compile Include="Implementation\Intellisense\Completion\BraceCompletionMetadata.cs" />
<Compile Include="Implementation\Intellisense\Completion\OptionSetExtensions.cs" /> <Compile Include="Implementation\Intellisense\Completion\OptionSetExtensions.cs" />
<Compile Include="Implementation\Intellisense\Completion\Presentation\ClassificationTags.cs" /> <Compile Include="Implementation\Intellisense\Completion\Presentation\ClassificationTags.cs" />
...@@ -279,7 +280,7 @@ ...@@ -279,7 +280,7 @@
<Compile Include="Implementation\Preview\PreviewReferenceHighlightingTaggerProvider.cs" /> <Compile Include="Implementation\Preview\PreviewReferenceHighlightingTaggerProvider.cs" />
<Compile Include="Implementation\Suggestions\FixAllSuggestedAction.FixAllCodeAction.cs" /> <Compile Include="Implementation\Suggestions\FixAllSuggestedAction.FixAllCodeAction.cs" />
<Compile Include="Implementation\Suggestions\FixMultipleOccurrencesService.cs" /> <Compile Include="Implementation\Suggestions\FixMultipleOccurrencesService.cs" />
<Compile Include="Implementation\GoToImplementation\GoToImplementationCommandHandler.cs" /> <Compile Include="GoToImplementation\GoToImplementationCommandHandler.cs" />
<Compile Include="Implementation\TodoComment\ITodoListProvider.cs" /> <Compile Include="Implementation\TodoComment\ITodoListProvider.cs" />
<Compile Include="Implementation\TodoComment\TodoItem.cs" /> <Compile Include="Implementation\TodoComment\TodoItem.cs" />
<Compile Include="Implementation\TodoComment\TodoItemsUpdatedArgs.cs" /> <Compile Include="Implementation\TodoComment\TodoItemsUpdatedArgs.cs" />
...@@ -376,9 +377,9 @@ ...@@ -376,9 +377,9 @@
<Compile Include="Implementation\EndConstructGeneration\IEndConstructGenerationService.cs" /> <Compile Include="Implementation\EndConstructGeneration\IEndConstructGenerationService.cs" />
<Compile Include="Implementation\ExtractInterface\AbstractExtractInterfaceCommandHandler.cs" /> <Compile Include="Implementation\ExtractInterface\AbstractExtractInterfaceCommandHandler.cs" />
<Compile Include="Implementation\ExtractMethod\AbstractExtractMethodCommandHandler.cs" /> <Compile Include="Implementation\ExtractMethod\AbstractExtractMethodCommandHandler.cs" />
<Compile Include="Implementation\FindReferences\AbstractFindReferencesService.cs" /> <Compile Include="FindReferences\AbstractFindReferencesService.cs" />
<Compile Include="Implementation\FindReferences\FindReferencesCommandHandler.cs" /> <Compile Include="FindReferences\FindReferencesCommandHandler.cs" />
<Compile Include="Implementation\FindReferences\IFindReferencesService.cs" /> <Compile Include="FindReferences\IFindReferencesService.cs" />
<Compile Include="Implementation\ForegroundNotification\ForegroundNotificationService.cs" /> <Compile Include="Implementation\ForegroundNotification\ForegroundNotificationService.cs" />
<Compile Include="Implementation\Formatting\FormatCommandHandler.cs" /> <Compile Include="Implementation\Formatting\FormatCommandHandler.cs" />
<Compile Include="Implementation\Formatting\FormatCommandHandler.ReturnKey.cs" /> <Compile Include="Implementation\Formatting\FormatCommandHandler.ReturnKey.cs" />
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
using Microsoft.CodeAnalysis.Navigation; using Microsoft.CodeAnalysis.Navigation;
using Roslyn.Utilities; using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Editor.Implementation.FindReferences namespace Microsoft.CodeAnalysis.Editor.FindReferences
{ {
internal abstract partial class AbstractFindReferencesService internal abstract partial class AbstractFindReferencesService
{ {
......
...@@ -3,18 +3,17 @@ ...@@ -3,18 +3,17 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor.FindUsages;
using Microsoft.CodeAnalysis.Editor.Host; using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Editor.SymbolMapping;
using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.CodeAnalysis.Navigation; using Microsoft.CodeAnalysis.Navigation;
using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities; using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Editor.Implementation.FindReferences namespace Microsoft.CodeAnalysis.Editor.FindReferences
{ {
internal abstract partial class AbstractFindReferencesService : internal abstract partial class AbstractFindReferencesService :
ForegroundThreadAffinitizedObject, IFindReferencesService, IStreamingFindReferencesService ForegroundThreadAffinitizedObject, IFindReferencesService, IStreamingFindReferencesService
...@@ -30,47 +29,13 @@ internal abstract partial class AbstractFindReferencesService : ...@@ -30,47 +29,13 @@ internal abstract partial class AbstractFindReferencesService :
_navigableItemPresenters = navigableItemPresenters; _navigableItemPresenters = navigableItemPresenters;
} }
/// <summary>
/// Common helper for both the synchronous and streaming versions of FAR.
/// It returns the symbol we want to search for and the solution we should
/// be searching.
///
/// Note that the <see cref="Solution"/> returned may absolutely *not* be
/// the same as <code>document.Project.Solution</code>. This is because
/// there may be symbol mapping involved (for example in Metadata-As-Source
/// scenarios).
/// </summary>
private async Task<Tuple<ISymbol, Project>> GetRelevantSymbolAndProjectAtPositionAsync(
Document document, int position, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var symbol = await SymbolFinder.FindSymbolAtPositionAsync(document, position, cancellationToken: cancellationToken).ConfigureAwait(false);
if (symbol == null)
{
return null;
}
// If this document is not in the primary workspace, we may want to search for results
// in a solution different from the one we started in. Use the starting workspace's
// ISymbolMappingService to get a context for searching in the proper solution.
var mappingService = document.Project.Solution.Workspace.Services.GetService<ISymbolMappingService>();
var mapping = await mappingService.MapSymbolAsync(document, symbol, cancellationToken).ConfigureAwait(false);
if (mapping == null)
{
return null;
}
return Tuple.Create(mapping.Symbol, mapping.Project);
}
private async Task<Tuple<IEnumerable<ReferencedSymbol>, Solution>> FindReferencedSymbolsAsync( private async Task<Tuple<IEnumerable<ReferencedSymbol>, Solution>> FindReferencedSymbolsAsync(
Document document, int position, IWaitContext waitContext) Document document, int position, IWaitContext waitContext)
{ {
var cancellationToken = waitContext.CancellationToken; var cancellationToken = waitContext.CancellationToken;
var symbolAndProject = await GetRelevantSymbolAndProjectAtPositionAsync(document, position, cancellationToken).ConfigureAwait(false); var symbolAndProject = await FindUsagesHelpers.GetRelevantSymbolAndProjectAtPositionAsync(
document, position, cancellationToken).ConfigureAwait(false);
if (symbolAndProject == null) if (symbolAndProject == null)
{ {
return null; return null;
...@@ -98,7 +63,6 @@ public bool TryFindReferences(Document document, int position, IWaitContext wait ...@@ -98,7 +63,6 @@ public bool TryFindReferences(Document document, int position, IWaitContext wait
{ {
var cancellationToken = waitContext.CancellationToken; var cancellationToken = waitContext.CancellationToken;
// Otherwise, fall back to displaying SymbolFinder based references.
var result = this.FindReferencedSymbolsAsync(document, position, waitContext).WaitAndGetResult(cancellationToken); var result = this.FindReferencedSymbolsAsync(document, position, waitContext).WaitAndGetResult(cancellationToken);
return TryDisplayReferences(result); return TryDisplayReferences(result);
} }
...@@ -164,7 +128,7 @@ private bool TryDisplayReferences(Tuple<IEnumerable<ReferencedSymbol>, Solution> ...@@ -164,7 +128,7 @@ private bool TryDisplayReferences(Tuple<IEnumerable<ReferencedSymbol>, Solution>
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
// Find the symbol we want to search and the solution we want to search in. // Find the symbol we want to search and the solution we want to search in.
var symbolAndProject = await GetRelevantSymbolAndProjectAtPositionAsync( var symbolAndProject = await FindUsagesHelpers.GetRelevantSymbolAndProjectAtPositionAsync(
document, position, cancellationToken).ConfigureAwait(false); document, position, cancellationToken).ConfigureAwait(false);
if (symbolAndProject == null) if (symbolAndProject == null)
{ {
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text;
using Roslyn.Utilities; using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Editor.Implementation.FindReferences namespace Microsoft.CodeAnalysis.Editor.FindReferences
{ {
[ExportCommandHandler(PredefinedCommandHandlerNames.FindReferences, ContentTypeNames.RoslynContentType)] [ExportCommandHandler(PredefinedCommandHandlerNames.FindReferences, ContentTypeNames.RoslynContentType)]
internal class FindReferencesCommandHandler : ICommandHandler<FindReferencesCommandArgs> internal class FindReferencesCommandHandler : ICommandHandler<FindReferencesCommandArgs>
...@@ -83,7 +83,7 @@ private bool TryExecuteCommand(int caretPosition, Document document) ...@@ -83,7 +83,7 @@ private bool TryExecuteCommand(int caretPosition, Document document)
var streamingEnabled = document.Project.Solution.Workspace.Options.GetOption(FeatureOnOffOptions.StreamingFindReferences, document.Project.Language); var streamingEnabled = document.Project.Solution.Workspace.Options.GetOption(FeatureOnOffOptions.StreamingFindReferences, document.Project.Language);
if (streamingEnabled && streamingService != null && streamingPresenter != null) if (streamingEnabled && streamingService != null && streamingPresenter != null)
{ {
StreamingFindReferences(document, streamingService, streamingPresenter, caretPosition); StreamingFindReferences(document, caretPosition, streamingService, streamingPresenter);
return true; return true;
} }
...@@ -113,8 +113,9 @@ private IStreamingFindUsagesPresenter GetStreamingPresenter() ...@@ -113,8 +113,9 @@ private IStreamingFindUsagesPresenter GetStreamingPresenter()
} }
private async void StreamingFindReferences( private async void StreamingFindReferences(
Document document, IStreamingFindReferencesService service, Document document, int caretPosition,
IStreamingFindUsagesPresenter presenter, int caretPosition) IStreamingFindReferencesService service,
IStreamingFindUsagesPresenter presenter)
{ {
try try
{ {
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // 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.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Roslyn.Utilities; using Roslyn.Utilities;
...@@ -14,6 +15,10 @@ protected FindUsagesContext() ...@@ -14,6 +15,10 @@ protected FindUsagesContext()
{ {
} }
public virtual void ReportMessage(string message)
{
}
public virtual void SetSearchLabel(string displayName) public virtual void SetSearchLabel(string displayName)
{ {
} }
......
// 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.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor.SymbolMapping;
using Microsoft.CodeAnalysis.FindSymbols;
namespace Microsoft.CodeAnalysis.Editor.FindUsages
{
internal static class FindUsagesHelpers
{
/// <summary>
/// Common helper for both the synchronous and streaming versions of FAR.
/// It returns the symbol we want to search for and the solution we should
/// be searching.
///
/// Note that the <see cref="Solution"/> returned may absolutely *not* be
/// the same as <code>document.Project.Solution</code>. This is because
/// there may be symbol mapping involved (for example in Metadata-As-Source
/// scenarios).
/// </summary>
public static async Task<Tuple<ISymbol, Project>> GetRelevantSymbolAndProjectAtPositionAsync(
Document document, int position, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var symbol = await SymbolFinder.FindSymbolAtPositionAsync(document, position, cancellationToken: cancellationToken).ConfigureAwait(false);
if (symbol == null)
{
return null;
}
// If this document is not in the primary workspace, we may want to search for results
// in a solution different from the one we started in. Use the starting workspace's
// ISymbolMappingService to get a context for searching in the proper solution.
var mappingService = document.Project.Solution.Workspace.Services.GetService<ISymbolMappingService>();
var mapping = await mappingService.MapSymbolAsync(document, symbol, cancellationToken).ConfigureAwait(false);
if (mapping == null)
{
return null;
}
return Tuple.Create(mapping.Symbol, mapping.Project);
}
}
}
\ No newline at end of file
...@@ -9,6 +9,14 @@ internal interface IFindUsagesContext ...@@ -9,6 +9,14 @@ internal interface IFindUsagesContext
{ {
CancellationToken CancellationToken { get; } CancellationToken CancellationToken { get; }
/// <summary>
/// Report a message to be displayed to the user.
/// </summary>
void ReportMessage(string message);
/// <summary>
/// Set the title of the window that results are displayed in.
/// </summary>
void SetSearchLabel(string displayName); void SetSearchLabel(string displayName);
Task OnDefinitionFoundAsync(DefinitionItem definition); Task OnDefinitionFoundAsync(DefinitionItem definition);
......
...@@ -2,9 +2,13 @@ ...@@ -2,9 +2,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor.FindUsages;
using Microsoft.CodeAnalysis.Editor.Host; using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Editor.Implementation.GoToDefinition;
using Microsoft.CodeAnalysis.Editor.SymbolMapping; using Microsoft.CodeAnalysis.Editor.SymbolMapping;
using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.FindUsages; using Microsoft.CodeAnalysis.FindUsages;
...@@ -13,48 +17,74 @@ ...@@ -13,48 +17,74 @@
using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities; using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Editor.Implementation.GoToImplementation namespace Microsoft.CodeAnalysis.Editor.GoToImplementation
{ {
internal abstract class AbstractGoToImplementationService : IGoToImplementationService internal abstract class AbstractGoToImplementationService :
IGoToImplementationService, IStreamingFindImplementationsService
{ {
private readonly IEnumerable<Lazy<INavigableItemsPresenter>> _navigableItemPresenters; private readonly IEnumerable<Lazy<INavigableItemsPresenter>> _navigableItemPresenters;
private readonly IEnumerable<Lazy<IStreamingFindUsagesPresenter>> _streamingPresenters;
public AbstractGoToImplementationService( public AbstractGoToImplementationService(
IEnumerable<Lazy<INavigableItemsPresenter>> navigableItemPresenters, IEnumerable<Lazy<INavigableItemsPresenter>> navigableItemPresenters)
IEnumerable<Lazy<IStreamingFindUsagesPresenter>> streamingPresenters)
{ {
_navigableItemPresenters = navigableItemPresenters; _navigableItemPresenters = navigableItemPresenters;
_streamingPresenters = streamingPresenters;
} }
public bool TryGoToImplementation(Document document, int position, CancellationToken cancellationToken, out string message) public bool TryGoToImplementation(Document document, int position, CancellationToken cancellationToken, out string message)
{ {
var symbol = SymbolFinder.FindSymbolAtPositionAsync(document, position, cancellationToken).WaitAndGetResult(cancellationToken); var result = this.FindImplementationsAsync(document, position, cancellationToken).WaitAndGetResult(cancellationToken);
if (symbol != null) if (result == null)
{ {
// Map the symbol if necessary back to the originating workspace if we're invoking from something message = EditorFeaturesResources.Cannot_navigate_to_the_symbol_under_the_caret;
// like metadata as source return false;
var mappingService = document.Project.Solution.Workspace.Services.GetRequiredService<ISymbolMappingService>(); }
var mapping = mappingService.MapSymbolAsync(document, symbol, cancellationToken).WaitAndGetResult(cancellationToken);
if (mapping != null) if (result.Value.message != null)
{ {
return TryGoToImplementationOnMappedSymbol(mapping, cancellationToken, out message); message = result.Value.message;
} return false;
}
return TryGoToImplementations(
result.Value.symbol, result.Value.project,
result.Value.implementations, cancellationToken, out message);
}
public async Task<(ISymbol symbol, Project project, ImmutableArray<ISymbol> implementations, string message)?> FindImplementationsAsync(Document document, int position, CancellationToken cancellationToken)
{
var symbolAndProject = await FindUsagesHelpers.GetRelevantSymbolAndProjectAtPositionAsync(
document, position, cancellationToken).ConfigureAwait(false);
if (symbolAndProject == null)
{
return null;
} }
message = EditorFeaturesResources.Cannot_navigate_to_the_symbol_under_the_caret; return await FindImplementationsAsync(
return false; symbolAndProject.Item1, symbolAndProject.Item2, cancellationToken).ConfigureAwait(false);
}
private async Task<(ISymbol symbol, Project project, ImmutableArray<ISymbol> implementations, string message)?> FindImplementationsAsync(
ISymbol symbol, Project project, CancellationToken cancellationToken)
{
var implementations = await FindImplementationsWorkerAsync(
symbol, project, cancellationToken).ConfigureAwait(false);
var filteredSymbols = implementations.WhereAsArray(
s => !s.IsAbstract && s.Locations.Any(l => l.IsInSource));
return filteredSymbols.Length == 0
? (symbol, project, filteredSymbols, EditorFeaturesResources.The_symbol_has_no_implementations)
: (symbol, project, filteredSymbols, null);
} }
private bool TryGoToImplementationOnMappedSymbol(SymbolMappingResult mapping, CancellationToken cancellationToken, out string message) private async Task<ImmutableArray<ISymbol>> FindImplementationsWorkerAsync(
ISymbol symbol, Project project, CancellationToken cancellationToken)
{ {
if (mapping.Symbol.IsInterfaceType() || mapping.Symbol.IsImplementableMember()) var solution = project.Solution;
if (symbol.IsInterfaceType() || symbol.IsImplementableMember())
{ {
var implementations = var implementations = await SymbolFinder.FindImplementationsAsync(
SymbolFinder.FindImplementationsAsync(mapping.Symbol, mapping.Solution, cancellationToken: cancellationToken) symbol, solution, cancellationToken: cancellationToken).ConfigureAwait(false);
.WaitAndGetResult(cancellationToken);
// It's important we use a HashSet here -- we may have cases in an inheritence hierarchy where more than one method // It's important we use a HashSet here -- we may have cases in an inheritence hierarchy where more than one method
// in an overrides chain implements the same interface method, and we want to duplicate those. The easiest way to do it // in an overrides chain implements the same interface method, and we want to duplicate those. The easiest way to do it
...@@ -69,125 +99,80 @@ private bool TryGoToImplementationOnMappedSymbol(SymbolMappingResult mapping, Ca ...@@ -69,125 +99,80 @@ private bool TryGoToImplementationOnMappedSymbol(SymbolMappingResult mapping, Ca
// of the method. We should also include those. // of the method. We should also include those.
if (implementation.IsOverridable()) if (implementation.IsOverridable())
{ {
implementationsAndOverrides.AddRange( var overrides = await SymbolFinder.FindOverridesAsync(
SymbolFinder.FindOverridesAsync(implementation, mapping.Solution, cancellationToken: cancellationToken).WaitAndGetResult(cancellationToken)); implementation, solution, cancellationToken: cancellationToken).ConfigureAwait(false);
implementationsAndOverrides.AddRange(overrides);
} }
} }
return TryGoToImplementations(implementationsAndOverrides, mapping, cancellationToken, out message); return implementationsAndOverrides.ToImmutableArray();
} }
else if ((mapping.Symbol as INamedTypeSymbol)?.TypeKind == TypeKind.Class) else if ((symbol as INamedTypeSymbol)?.TypeKind == TypeKind.Class)
{ {
var implementations = var derivedClasses = await SymbolFinder.FindDerivedClassesAsync(
SymbolFinder.FindDerivedClassesAsync((INamedTypeSymbol)mapping.Symbol, mapping.Solution, cancellationToken: cancellationToken) (INamedTypeSymbol)symbol, solution, cancellationToken: cancellationToken).ConfigureAwait(false);
.WaitAndGetResult(cancellationToken) var implementations = derivedClasses.Concat(symbol);
.Concat(mapping.Symbol);
return TryGoToImplementations(implementations, mapping, cancellationToken, out message); return implementations.ToImmutableArray();
} }
else if (mapping.Symbol.IsOverridable()) else if (symbol.IsOverridable())
{ {
var implementations = var overrides = await SymbolFinder.FindOverridesAsync(
SymbolFinder.FindOverridesAsync(mapping.Symbol, mapping.Solution, cancellationToken: cancellationToken) symbol, solution, cancellationToken: cancellationToken).ConfigureAwait(false);
.WaitAndGetResult(cancellationToken) var implementations = overrides.Concat(symbol);
.Concat(mapping.Symbol);
return TryGoToImplementations(implementations, mapping, cancellationToken, out message); return implementations.ToImmutableArray();
} }
else else
{ {
// This is something boring like a regular method or type, so we'll just go there directly // This is something boring like a regular method or type, so we'll just go there directly
if (GoToDefinition.GoToDefinitionHelpers.TryGoToDefinition( return ImmutableArray.Create(symbol);
mapping.Symbol, mapping.Project, //if (GoToDefinitionHelpers.TryGoToDefinition(
_navigableItemPresenters, _streamingPresenters, cancellationToken)) // symbol, project,
{ // _navigableItemPresenters, _streamingPresenters, cancellationToken))
message = null; //{
return true; // message = null;
} // return true;
//}
message = EditorFeaturesResources.Cannot_navigate_to_the_symbol_under_the_caret;
return false; //message = EditorFeaturesResources.Cannot_navigate_to_the_symbol_under_the_caret;
//return false;
} }
} }
private bool TryGoToImplementations(IEnumerable<ISymbol> candidateImplementations, SymbolMappingResult mapping, CancellationToken cancellationToken, out string message) private bool TryGoToImplementations(
ISymbol symbol, Project project, ImmutableArray<ISymbol> implementations, CancellationToken cancellationToken, out string message)
{ {
var implementations = candidateImplementations if (implementations.Length == 0)
.Where(s => !s.IsAbstract && s.Locations.Any(l => l.IsInSource))
.ToList();
if (implementations.Count == 0)
{ {
message = EditorFeaturesResources.The_symbol_has_no_implementations; message = EditorFeaturesResources.The_symbol_has_no_implementations;
return false; return false;
} }
else if (implementations.Count == 1) else if (implementations.Length == 1)
{ {
GoToDefinition.GoToDefinitionHelpers.TryGoToDefinition( GoToDefinitionHelpers.TryGoToDefinition(
implementations.Single(), mapping.Project, implementations.Single(), project, _navigableItemPresenters,
_navigableItemPresenters, _streamingPresenters, cancellationToken); SpecializedCollections.EmptyEnumerable<Lazy<IStreamingFindUsagesPresenter>>(),
cancellationToken);
message = null; message = null;
return true; return true;
} }
else else
{ {
return TryPresentInFindUsagesPresenter(mapping, implementations, cancellationToken, out message) || return TryPresentInNavigableItemsPresenter(symbol, project, implementations, out message);
TryPresentInNavigableItemsPresenter(mapping, implementations, out message);
}
}
private bool TryPresentInFindUsagesPresenter(
SymbolMappingResult mapping, List<ISymbol> implementations, CancellationToken cancellationToken, out string message)
{
message = null;
var presenter = GetFindUsagesPresenter();
if (presenter == null)
{
return false;
}
var definitionItems = implementations.Select(s =>
s.ToDefinitionItem(mapping.Solution)).ToImmutableArrayOrEmpty();
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( private bool TryPresentInNavigableItemsPresenter(
SymbolMappingResult mapping, List<ISymbol> implementations, out string message) ISymbol symbol, Project project, ImmutableArray<ISymbol> implementations, out string message)
{ {
// We have multiple symbols, so we'll build a list of all preferred locations for all the symbols // We have multiple symbols, so we'll build a list of all preferred locations for all the symbols
var navigableItems = implementations.SelectMany( var navigableItems = implementations.SelectMany(
implementation => CreateItemsForImplementation(implementation, mapping.Solution)); implementation => CreateItemsForImplementation(implementation, project.Solution));
var presenter = _navigableItemPresenters.First(); var presenter = _navigableItemPresenters.First();
var taggedParts = NavigableItemFactory.GetSymbolDisplayTaggedParts(mapping.Project, mapping.Symbol); var taggedParts = NavigableItemFactory.GetSymbolDisplayTaggedParts(project, symbol);
presenter.Value.DisplayResult(taggedParts.JoinText(), navigableItems); presenter.Value.DisplayResult(taggedParts.JoinText(), navigableItems);
message = null; message = null;
...@@ -203,5 +188,32 @@ private static IEnumerable<INavigableItem> CreateItemsForImplementation(ISymbol ...@@ -203,5 +188,32 @@ private static IEnumerable<INavigableItem> CreateItemsForImplementation(ISymbol
implementation, implementation,
displayTaggedParts: symbolDisplayService.ToDisplayParts(implementation).ToTaggedText()); displayTaggedParts: symbolDisplayService.ToDisplayParts(implementation).ToTaggedText());
} }
public async Task FindImplementationsAsync(Document document, int position, IFindUsagesContext context)
{
var tuple = await FindImplementationsAsync(
document, position, context.CancellationToken).ConfigureAwait(false);
if (tuple == null)
{
context.ReportMessage(EditorFeaturesResources.Cannot_navigate_to_the_symbol_under_the_caret);
return;
}
var message = tuple.Value.message;
if (message != null)
{
context.ReportMessage(message);
return;
}
var project = tuple.Value.project;
foreach (var implementation in tuple.Value.implementations)
{
var definitionItem = implementation.ToDefinitionItem(project.Solution);
await context.OnDefinitionFoundAsync(definitionItem).ConfigureAwait(false);
}
}
} }
} }
\ 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;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor.Commands;
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.Editor.Shared.Options;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.CodeAnalysis.Notification;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Editor.GoToImplementation
{
[ExportCommandHandler(PredefinedCommandHandlerNames.GoToImplementation,
ContentTypeNames.RoslynContentType)]
internal sealed class GoToImplementationCommandHandler : ICommandHandler<GoToImplementationCommandArgs>
{
private readonly IWaitIndicator _waitIndicator;
private readonly IEnumerable<Lazy<IStreamingFindUsagesPresenter>> _streamingPresenters;
private readonly IAsynchronousOperationListener _asyncListener;
[ImportingConstructor]
public GoToImplementationCommandHandler(
IWaitIndicator waitIndicator,
[ImportMany] IEnumerable<Lazy<IStreamingFindUsagesPresenter>> streamingPresenters,
[ImportMany] IEnumerable<Lazy<IAsynchronousOperationListener, FeatureMetadata>> asyncListeners)
{
_waitIndicator = waitIndicator;
_streamingPresenters = streamingPresenters;
_asyncListener = new AggregateAsynchronousOperationListener(
asyncListeners, FeatureAttribute.GoToImplementation);
}
public CommandState GetCommandState(GoToImplementationCommandArgs args, Func<CommandState> nextHandler)
{
// Because this is expensive to compute, we just always say yes
return CommandState.Available;
}
public void ExecuteCommand(GoToImplementationCommandArgs args, Action nextHandler)
{
var caret = args.TextView.GetCaretPoint(args.SubjectBuffer);
if (caret.HasValue)
{
var document = args.SubjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges();
if (document != null)
{
ExecuteCommand(document, caret.Value);
return;
}
}
nextHandler();
}
private void ExecuteCommand(Document document, int caretPosition)
{
var streamingService = document.Project.LanguageServices.GetService<IStreamingFindImplementationsService>();
var synchronousService = document.Project.LanguageServices.GetService<IGoToImplementationService>();
var streamingPresenter = GetStreamingPresenter();
// See if we're running on a host that can provide streaming results.
// We'll both need a FAR service that can stream results to us, and
// a presenter that can accept streamed results.
var streamingEnabled = document.Project.Solution.Workspace.Options.GetOption(FeatureOnOffOptions.StreamingGoToImplementation, document.Project.Language);
var canUseStreamingWindow = streamingEnabled && streamingService != null && streamingPresenter != null;
var canUseSynchronousWindow = synchronousService != null;
if (canUseStreamingWindow || canUseSynchronousWindow)
{
// We have all the cheap stuff, so let's do expensive stuff now
string messageToShow = null;
_waitIndicator.Wait(
EditorFeaturesResources.Go_To_Implementation,
EditorFeaturesResources.Locating_implementations,
allowCancel: true,
action: context =>
{
if (canUseStreamingWindow)
{
StreamingGoToImplementation(
document, caretPosition,
streamingService, streamingPresenter,
context.CancellationToken, out messageToShow);
}
else
{
synchronousService.TryGoToImplementation(
document, caretPosition, context.CancellationToken, out messageToShow);
}
});
if (messageToShow != null)
{
var notificationService = document.Project.Solution.Workspace.Services.GetService<INotificationService>();
notificationService.SendNotification(messageToShow,
title: EditorFeaturesResources.Go_To_Implementation,
severity: NotificationSeverity.Information);
}
}
}
private void StreamingGoToImplementation(
Document document, int caretPosition,
IStreamingFindImplementationsService streamingService,
IStreamingFindUsagesPresenter streamingPresenter,
CancellationToken cancellationToken,
out string messageToShow)
{
var goToImplContext = new GoToImplementationContext(cancellationToken);
streamingService.FindImplementationsAsync(document, caretPosition, goToImplContext).Wait(cancellationToken);
// If finding implementations reported a message, then just stop and show that
// message to the user.
messageToShow = goToImplContext.Message;
if (messageToShow != null)
{
return;
}
// Ignore any definitions that we can't navigate to.
var definitions = goToImplContext.GetDefinitionItems()
.WhereAsArray(d => d.CanNavigateTo());
// See if there's a third party external item we can navigate to. If so, defer
// to that item and finish.
var externalItems = definitions.WhereAsArray(d => d.IsExternal);
foreach (var item in externalItems)
{
if (item.TryNavigateTo())
{
return;
}
}
var nonExternalItems = definitions.WhereAsArray(d => !d.IsExternal);
if (nonExternalItems.Length == 0)
{
return;
}
if (nonExternalItems.Length == 1 &&
nonExternalItems[0].SourceSpans.Length <= 1)
{
// There was only one location to navigate to. Just directly go to that location.
nonExternalItems[0].TryNavigateTo();
return;
}
// We have multiple definitions, or we have definitions with multiple locations.
// Present this to the user so they can decide where they want to go to.
var context = streamingPresenter.StartSearch(EditorFeaturesResources.Go_To_Implementation);
foreach (var definition in nonExternalItems)
{
context.OnDefinitionFoundAsync(definition).Wait(cancellationToken);
}
// Note: we don't need to put this in a finally. The only time we might not hit
// this is if cancellation or another error gets thrown. In the former case,
// that means that a new search has started. We don't care about telling the
// 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.
context.OnCompletedAsync().Wait(cancellationToken);
}
private IStreamingFindUsagesPresenter GetStreamingPresenter()
{
try
{
return _streamingPresenters.FirstOrDefault()?.Value;
}
catch
{
return null;
}
}
private class GoToImplementationContext : FindUsagesContext
{
private readonly object _gate = new object();
private readonly ImmutableArray<DefinitionItem>.Builder _definitionItems =
ImmutableArray.CreateBuilder<DefinitionItem>();
public override CancellationToken CancellationToken { get; }
public GoToImplementationContext(CancellationToken cancellationToken)
{
CancellationToken = cancellationToken;
}
public string Message { get; private set; }
public override void ReportMessage(string message)
=> Message = message;
public ImmutableArray<DefinitionItem> GetDefinitionItems()
{
lock (_gate)
{
return _definitionItems.ToImmutableArray();
}
}
public override Task OnDefinitionFoundAsync(DefinitionItem definition)
{
lock (_gate)
{
_definitionItems.Add(definition);
}
return SpecializedTasks.EmptyTask;
}
}
}
}
\ 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. // 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.Threading; using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host;
namespace Microsoft.CodeAnalysis.Editor namespace Microsoft.CodeAnalysis.Editor
...@@ -14,4 +16,9 @@ internal interface IGoToImplementationService : ILanguageService ...@@ -14,4 +16,9 @@ internal interface IGoToImplementationService : ILanguageService
/// <returns>True if navigating to the implementation of the symbol at the provided position succeeds. False, otherwise.</returns> /// <returns>True if navigating to the implementation of the symbol at the provided position succeeds. False, otherwise.</returns>
bool TryGoToImplementation(Document document, int position, CancellationToken cancellationToken, out string message); bool TryGoToImplementation(Document document, int position, CancellationToken cancellationToken, out string message);
} }
internal interface IStreamingFindImplementationsService : ILanguageService
{
Task FindImplementationsAsync(Document document, int position, IFindUsagesContext context);
}
} }
// 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.ComponentModel.Composition;
using Microsoft.CodeAnalysis.Editor.Commands;
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.Notification;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.Editor.Implementation.GoToImplementation
{
[ExportCommandHandler(PredefinedCommandHandlerNames.GoToImplementation,
ContentTypeNames.RoslynContentType)]
internal sealed class GoToImplementationCommandHandler : ICommandHandler<GoToImplementationCommandArgs>
{
private readonly IWaitIndicator _waitIndicator;
[ImportingConstructor]
public GoToImplementationCommandHandler(
IWaitIndicator waitIndicator)
{
_waitIndicator = waitIndicator;
}
public CommandState GetCommandState(GoToImplementationCommandArgs args, Func<CommandState> nextHandler)
{
// Because this is expensive to compute, we just always say yes
return CommandState.Available;
}
public void ExecuteCommand(GoToImplementationCommandArgs args, Action nextHandler)
{
var caret = args.TextView.GetCaretPoint(args.SubjectBuffer);
if (caret.HasValue)
{
var document = args.SubjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges();
if (document != null)
{
var service = document.Project.LanguageServices.GetService<IGoToImplementationService>();
if (service != null)
{
// We have all the cheap stuff, so let's do expensive stuff now
string messageToShow = null;
bool succeeded = false;
_waitIndicator.Wait(
EditorFeaturesResources.Go_To_Implementation,
EditorFeaturesResources.Locating_implementations,
allowCancel: true,
action: context => succeeded = service.TryGoToImplementation(document, caret.Value, context.CancellationToken, out messageToShow));
if (messageToShow != null)
{
var notificationService = document.Project.Solution.Workspace.Services.GetService<INotificationService>();
notificationService.SendNotification(messageToShow,
title: EditorFeaturesResources.Go_To_Implementation,
severity: NotificationSeverity.Information);
}
}
return;
}
}
nextHandler();
}
}
}
...@@ -87,9 +87,15 @@ internal static class FeatureOnOffOptions ...@@ -87,9 +87,15 @@ internal static class FeatureOnOffOptions
/// implemented this feature yet. /// implemented this feature yet.
/// </summary> /// </summary>
[ExportOption] [ExportOption]
public static readonly PerLanguageOption<bool> RefactoringVerification = new PerLanguageOption<bool>(nameof(FeatureOnOffOptions), nameof(RefactoringVerification), defaultValue: false); public static readonly PerLanguageOption<bool> RefactoringVerification = new PerLanguageOption<bool>(
nameof(FeatureOnOffOptions), nameof(RefactoringVerification), defaultValue: false);
[ExportOption] [ExportOption]
public static readonly PerLanguageOption<bool> StreamingFindReferences = new PerLanguageOption<bool>(nameof(FeatureOnOffOptions), nameof(StreamingFindReferences), defaultValue: true); public static readonly PerLanguageOption<bool> StreamingFindReferences = new PerLanguageOption<bool>(
nameof(FeatureOnOffOptions), nameof(StreamingFindReferences), defaultValue: true);
[ExportOption]
public static readonly PerLanguageOption<bool> StreamingGoToImplementation = new PerLanguageOption<bool>(
nameof(FeatureOnOffOptions), nameof(StreamingGoToImplementation), defaultValue: true);
} }
} }
\ No newline at end of file
...@@ -9,15 +9,20 @@ internal partial class FeatureAttribute ...@@ -9,15 +9,20 @@ internal partial class FeatureAttribute
public const string BraceHighlighting = nameof(BraceHighlighting); public const string BraceHighlighting = nameof(BraceHighlighting);
public const string CallHierarchy = nameof(CallHierarchy); public const string CallHierarchy = nameof(CallHierarchy);
public const string Classification = nameof(Classification); public const string Classification = nameof(Classification);
public const string CodeModel = nameof(CodeModel);
public const string CompletionSet = nameof(CompletionSet); public const string CompletionSet = nameof(CompletionSet);
public const string DesignerAttribute = nameof(DesignerAttribute); public const string DesignerAttribute = nameof(DesignerAttribute);
public const string ErrorSquiggles = nameof(ErrorSquiggles); public const string DiagnosticService = nameof(DiagnosticService);
public const string ErrorList = nameof(ErrorList); public const string ErrorList = nameof(ErrorList);
public const string FindReferences = nameof(FindReferences); public const string ErrorSquiggles = nameof(ErrorSquiggles);
public const string TodoCommentList = nameof(TodoCommentList);
public const string EventHookup = nameof(EventHookup); public const string EventHookup = nameof(EventHookup);
public const string FindReferences = nameof(FindReferences);
public const string GlobalOperation = nameof(GlobalOperation);
public const string GoToImplementation = nameof(GoToImplementation);
public const string GraphProvider = nameof(GraphProvider); public const string GraphProvider = nameof(GraphProvider);
public const string InfoBar = nameof(InfoBar);
public const string KeywordHighlighting = nameof(KeywordHighlighting); public const string KeywordHighlighting = nameof(KeywordHighlighting);
public const string LightBulb = nameof(LightBulb);
public const string LineSeparators = nameof(LineSeparators); public const string LineSeparators = nameof(LineSeparators);
public const string NavigateTo = nameof(NavigateTo); public const string NavigateTo = nameof(NavigateTo);
public const string NavigationBar = nameof(NavigationBar); public const string NavigationBar = nameof(NavigationBar);
...@@ -26,15 +31,11 @@ internal partial class FeatureAttribute ...@@ -26,15 +31,11 @@ internal partial class FeatureAttribute
public const string ReferenceHighlighting = nameof(ReferenceHighlighting); public const string ReferenceHighlighting = nameof(ReferenceHighlighting);
public const string Rename = nameof(Rename); public const string Rename = nameof(Rename);
public const string RenameTracking = nameof(RenameTracking); public const string RenameTracking = nameof(RenameTracking);
public const string RuleSetEditor = nameof(RuleSetEditor);
public const string SignatureHelp = nameof(SignatureHelp); public const string SignatureHelp = nameof(SignatureHelp);
public const string Snippets = nameof(Snippets); public const string Snippets = nameof(Snippets);
public const string SolutionCrawler = nameof(SolutionCrawler); public const string SolutionCrawler = nameof(SolutionCrawler);
public const string TodoCommentList = nameof(TodoCommentList);
public const string Workspace = nameof(Workspace); public const string Workspace = nameof(Workspace);
public const string LightBulb = nameof(LightBulb);
public const string CodeModel = nameof(CodeModel);
public const string GlobalOperation = nameof(GlobalOperation);
public const string DiagnosticService = nameof(DiagnosticService);
public const string RuleSetEditor = nameof(RuleSetEditor);
public const string InfoBar = nameof(InfoBar);
} }
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册