|
// 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.ComponentModel.Composition;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Navigation;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.VisualStudio.Shell.TableControl;
using Microsoft.VisualStudio.Text.Classification;
using Microsoft.VisualStudio.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.FindUsages;
/// <summary>
/// Event processor that we export so we can control how navigation works in the streaming
/// FAR window. We need this because the FAR window has no way to know how to do things like
/// navigate to definitions that are from metadata. We take control here and handle navigation
/// ourselves so that we can do things like navigate to MetadataAsSource.
/// </summary>
[Export(typeof(ITableControlEventProcessorProvider))]
[DataSourceType(StreamingFindUsagesPresenter.RoslynFindUsagesTableDataSourceSourceTypeIdentifier)]
[DataSource(StreamingFindUsagesPresenter.RoslynFindUsagesTableDataSourceIdentifier)]
[Name(nameof(FindUsagesTableControlEventProcessorProvider))]
[Order(Before = Priority.Default)]
internal class FindUsagesTableControlEventProcessorProvider : ITableControlEventProcessorProvider
{
private readonly IUIThreadOperationExecutor _operationExecutor;
private readonly IAsynchronousOperationListener _listener;
[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public FindUsagesTableControlEventProcessorProvider(
IUIThreadOperationExecutor operationExecutor,
IAsynchronousOperationListenerProvider asyncProvider)
{
_operationExecutor = operationExecutor;
_listener = asyncProvider.GetListener(FeatureAttribute.FindReferences);
}
public ITableControlEventProcessor GetAssociatedEventProcessor(IWpfTableControl tableControl)
=> new TableControlEventProcessor(_operationExecutor, _listener);
private class TableControlEventProcessor : TableControlEventProcessorBase
{
private readonly IUIThreadOperationExecutor _operationExecutor;
private readonly IAsynchronousOperationListener _listener;
public TableControlEventProcessor(IUIThreadOperationExecutor operationExecutor, IAsynchronousOperationListener listener)
{
_operationExecutor = operationExecutor;
_listener = listener;
}
public override void PreprocessNavigate(ITableEntryHandle entry, TableEntryNavigateEventArgs e)
{
var supportsNavigation = entry.Identity as ISupportsNavigation ??
(entry.TryGetValue(StreamingFindUsagesPresenter.SelfKeyName, out var item) ? item as ISupportsNavigation : null);
if (supportsNavigation != null &&
supportsNavigation.CanNavigateTo())
{
// Fire and forget
e.Handled = true;
_ = ProcessNavigateAsync(supportsNavigation, e, _listener, _operationExecutor);
}
base.PreprocessNavigate(entry, e);
return;
async static Task ProcessNavigateAsync(
ISupportsNavigation supportsNavigation, TableEntryNavigateEventArgs e,
IAsynchronousOperationListener listener,
IUIThreadOperationExecutor operationExecutor)
{
using var token = listener.BeginAsyncOperation(nameof(ProcessNavigateAsync));
using var context = operationExecutor.BeginExecute(
ServicesVSResources.IntelliSense,
EditorFeaturesResources.Navigating,
allowCancellation: true,
showProgress: false);
var options = new NavigationOptions(PreferProvisionalTab: e.IsPreview, ActivateTab: e.ShouldActivate);
await supportsNavigation.NavigateToAsync(options, context.UserCancellationToken).ConfigureAwait(false);
}
}
}
}
|