|
// 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.Immutable;
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript.Api;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.NavigateTo;
using Microsoft.CodeAnalysis.Navigation;
using Microsoft.CodeAnalysis.PatternMatching;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript;
[ExportLanguageService(typeof(INavigateToSearchService), InternalLanguageNames.TypeScript), Shared]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal sealed class VSTypeScriptNavigateToSearchService(
[Import(AllowDefault = true)] IVSTypeScriptNavigateToSearchService? searchService) : INavigateToSearchService
{
private readonly IVSTypeScriptNavigateToSearchService? _searchService = searchService;
public IImmutableSet<string> KindsProvided => _searchService?.KindsProvided ?? ImmutableHashSet<string>.Empty;
public bool CanFilter => _searchService?.CanFilter ?? false;
public async Task SearchDocumentAsync(
Document document,
string searchPattern,
IImmutableSet<string> kinds,
Func<ImmutableArray<INavigateToSearchResult>, Task> onResultsFound,
CancellationToken cancellationToken)
{
if (_searchService != null)
{
var results = await _searchService.SearchDocumentAsync(document, searchPattern, kinds, cancellationToken).ConfigureAwait(false);
if (results.Length > 0)
await onResultsFound(results.SelectAsArray(Convert)).ConfigureAwait(false);
}
}
public async Task SearchProjectsAsync(
Solution solution,
ImmutableArray<Project> projects,
ImmutableArray<Document> priorityDocuments,
string searchPattern,
IImmutableSet<string> kinds,
Document? activeDocument,
Func<ImmutableArray<INavigateToSearchResult>, Task> onResultsFound,
Func<Task> onProjectCompleted,
CancellationToken cancellationToken)
{
Contract.ThrowIfTrue(projects.IsEmpty);
Contract.ThrowIfTrue(projects.Select(p => p.Language).Distinct().Count() != 1);
using var _ = PooledHashSet<Project>.GetInstance(out var processedProjects);
foreach (var group in priorityDocuments.GroupBy(d => d.Project))
await ProcessProjectAsync(group.Key).ConfigureAwait(false);
foreach (var project in projects)
await ProcessProjectAsync(project).ConfigureAwait(false);
return;
async Task ProcessProjectAsync(Project project)
{
if (processedProjects.Add(project))
{
if (_searchService != null)
{
var results = await _searchService.SearchProjectAsync(
project, priorityDocuments.WhereAsArray(d => d.Project == project), searchPattern, kinds, cancellationToken).ConfigureAwait(false);
if (results.Length > 0)
await onResultsFound(results.SelectAsArray(Convert)).ConfigureAwait(false);
}
await onProjectCompleted().ConfigureAwait(false);
}
}
}
private static INavigateToSearchResult Convert(IVSTypeScriptNavigateToSearchResult result)
=> new WrappedNavigateToSearchResult(result);
private sealed class WrappedNavigateToSearchResult(IVSTypeScriptNavigateToSearchResult result) : INavigateToSearchResult
{
private readonly IVSTypeScriptNavigateToSearchResult _result = result;
public string AdditionalInformation => _result.AdditionalInformation;
public string Kind => _result.Kind;
public NavigateToMatchKind MatchKind
=> _result.MatchKind switch
{
VSTypeScriptNavigateToMatchKind.Exact => NavigateToMatchKind.Exact,
VSTypeScriptNavigateToMatchKind.Prefix => NavigateToMatchKind.Prefix,
VSTypeScriptNavigateToMatchKind.Substring => NavigateToMatchKind.Substring,
VSTypeScriptNavigateToMatchKind.Regular => NavigateToMatchKind.Regular,
VSTypeScriptNavigateToMatchKind.None => NavigateToMatchKind.None,
VSTypeScriptNavigateToMatchKind.CamelCaseExact => NavigateToMatchKind.CamelCaseExact,
VSTypeScriptNavigateToMatchKind.CamelCasePrefix => NavigateToMatchKind.CamelCasePrefix,
VSTypeScriptNavigateToMatchKind.CamelCaseNonContiguousPrefix => NavigateToMatchKind.CamelCaseNonContiguousPrefix,
VSTypeScriptNavigateToMatchKind.CamelCaseSubstring => NavigateToMatchKind.CamelCaseSubstring,
VSTypeScriptNavigateToMatchKind.CamelCaseNonContiguousSubstring => NavigateToMatchKind.CamelCaseNonContiguousSubstring,
VSTypeScriptNavigateToMatchKind.Fuzzy => NavigateToMatchKind.Fuzzy,
_ => throw ExceptionUtilities.UnexpectedValue(_result.MatchKind),
};
public bool IsCaseSensitive => _result.IsCaseSensitive;
public string Name => _result.Name;
public ImmutableArray<TextSpan> NameMatchSpans => _result.NameMatchSpans;
public string SecondarySort => _result.SecondarySort;
public string Summary => _result.Summary;
public INavigableItem NavigableItem => new VSTypeScriptNavigableItemWrapper(_result.NavigableItem);
public ImmutableArray<PatternMatch> Matches => NavigateToSearchResultHelpers.GetMatches(this);
}
}
|