File: NavigateTo\NavigateToItemProvider.cs
Web Access
Project: src\src\EditorFeatures\Core.Wpf\Microsoft.CodeAnalysis.EditorFeatures.Wpf_tpal30ww_wpftmp.csproj (Microsoft.CodeAnalysis.EditorFeatures.Wpf)
// 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.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.NavigateTo;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.VisualStudio.Language.NavigateTo.Interfaces;
using Microsoft.VisualStudio.Utilities;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.Editor.Implementation.NavigateTo;
 
internal sealed partial class NavigateToItemProvider : INavigateToItemProvider2
{
    private readonly Workspace _workspace;
    private readonly IAsynchronousOperationListener _asyncListener;
    private readonly INavigateToItemDisplayFactory _displayFactory;
    private readonly IThreadingContext _threadingContext;
 
    private CancellationTokenSource _cancellationTokenSource = new();
 
    public NavigateToItemProvider(
        Workspace workspace,
        IThreadingContext threadingContext,
        IUIThreadOperationExecutor threadOperationExecutor,
        IAsynchronousOperationListener asyncListener)
    {
        Contract.ThrowIfNull(workspace);
        Contract.ThrowIfNull(asyncListener);
 
        _workspace = workspace;
        _asyncListener = asyncListener;
        _displayFactory = new NavigateToItemDisplayFactory(
            threadingContext, threadOperationExecutor, asyncListener);
        _threadingContext = threadingContext;
    }
 
    ISet<string> INavigateToItemProvider2.KindsProvided => KindsProvided;
 
    public ImmutableHashSet<string> KindsProvided
        => NavigateToUtilities.GetKindsProvided(_workspace.CurrentSolution);
 
    public bool CanFilter
    {
        get
        {
            foreach (var project in _workspace.CurrentSolution.Projects)
            {
                var navigateToSearchService = project.GetLanguageService<INavigateToSearchService>();
                if (navigateToSearchService is null)
                {
                    // If we reach here, it means the current project does not support Navigate To, which is
                    // functionally equivalent to supporting filtering.
                    continue;
                }
 
                if (!navigateToSearchService.CanFilter)
                {
                    return false;
                }
            }
 
            // All projects either support filtering or do not support Navigate To at all
            return true;
        }
    }
 
    public void StopSearch()
    {
        _cancellationTokenSource.Cancel();
        _cancellationTokenSource = new CancellationTokenSource();
    }
 
    public void Dispose()
    {
        this.StopSearch();
        (_displayFactory as IDisposable)?.Dispose();
    }
 
    public void StartSearch(INavigateToCallback callback, string searchValue)
        => StartSearch(callback, searchValue, KindsProvided);
 
    public void StartSearch(INavigateToCallback callback, string searchValue, INavigateToFilterParameters filter)
        => StartSearch(callback, searchValue, filter.Kinds.ToImmutableHashSet(StringComparer.Ordinal));
 
    private void StartSearch(INavigateToCallback callback, string searchValue, IImmutableSet<string> kinds)
    {
        this.StopSearch();
 
        if (string.IsNullOrWhiteSpace(searchValue))
        {
            callback.Done();
            return;
        }
 
        if (kinds == null || kinds.Count == 0)
        {
            kinds = KindsProvided;
        }
 
        var searchCurrentDocument = (callback.Options as INavigateToOptions2)?.SearchCurrentDocument ?? false;
        var searchScope = searchCurrentDocument
            ? NavigateToSearchScope.Document
            : NavigateToSearchScope.Solution;
 
        var solution = _workspace.CurrentSolution;
        var roslynCallback = new NavigateToItemProviderCallback(solution, _displayFactory, callback);
        var searcher = NavigateToSearcher.Create(
            solution,
            _asyncListener,
            roslynCallback,
            searchValue,
            kinds,
            _threadingContext.DisposalToken);
 
        var asyncToken = _asyncListener.BeginAsyncOperation(nameof(StartSearch));
        _ = searcher.SearchAsync(searchScope, _cancellationTokenSource.Token)
            .CompletesAsyncOperation(asyncToken)
            .ReportNonFatalErrorUnlessCancelledAsync(_cancellationTokenSource.Token);
    }
}