File: FindReferences\FindReferencesTableControlEventProcessorProvider.cs
Web Access
Project: src\src\VisualStudio\Core\Def\Microsoft.VisualStudio.LanguageServices_pxr0p0dn_wpftmp.csproj (Microsoft.VisualStudio.LanguageServices)
// 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(
                    EditorFeaturesResources.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);
            }
        }
    }
}