File: SymbolSearch\ISymbolSearchService.cs
Web Access
Project: src\src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces)
// 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.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.SymbolSearch;
 
/// <param name="Name">The roslyn simple name of the type to search for. For example <see cref="ImmutableArray{T}"/>
/// would have the name <c>ImmutableArray</c></param>
/// <param name="Arity">The arity of the type.  For example <see cref="ImmutableArray{T}"/> would have arity
/// <c>1</c></param>.
[DataContract]
internal readonly record struct TypeQuery(
    [property: DataMember(Order = 0)] string Name,
    [property: DataMember(Order = 1)] int Arity)
{
    public static readonly TypeQuery Default = default;
 
    public bool IsDefault => string.IsNullOrEmpty(Name);
}
 
/// <param name="Names">The names comprising the namespace being searched for.  For example <c>["System", "Collections",
/// "Immutable"]</c>.</param>
[DataContract]
internal readonly record struct NamespaceQuery(
    [property: DataMember(Order = 0)] ImmutableArray<string> Names)
{
    public static readonly NamespaceQuery Default = default;
 
    public static implicit operator NamespaceQuery(ImmutableArray<string> names)
        => new(names);
 
    public bool IsDefault => Names.IsDefaultOrEmpty;
}
 
internal interface ISymbolSearchService : IWorkspaceService
{
    /// <summary>
    /// Searches for packages that contain a type with the provided name and arity. Note: Implementations are free to
    /// return the results they feel best for the given data.  Specifically, they can do exact or fuzzy matching on the
    /// name. They can use or ignore the arity depending on their capabilities. 
    /// 
    /// Implementations should return results in order from best to worst (from their perspective).
    /// </summary>
    ValueTask<ImmutableArray<PackageResult>> FindPackagesAsync(
        string source, TypeQuery typeQuery, NamespaceQuery namespaceQuery, CancellationToken cancellationToken);
 
    /// <summary>
    /// Searches for packages that contain an assembly with the provided name. Note: Implementations are free to return
    /// the results they feel best for the given data.  Specifically, they can do exact or fuzzy matching on the name.
    /// 
    /// Implementations should return results in order from best to worst (from their perspective).
    /// </summary>
    ValueTask<ImmutableArray<PackageWithAssemblyResult>> FindPackagesWithAssemblyAsync(
        string source, string assemblyName, CancellationToken cancellationToken);
 
    /// <summary>
    /// Searches for reference assemblies that contain a type with the provided name and arity.
    /// Note: Implementations are free to return the results they feel best for the
    /// given data.  Specifically, they can do exact or fuzzy matching on the name.
    /// They can use or ignore the arity depending on their capabilities. 
    /// 
    /// Implementations should return results in order from best to worst (from their
    /// perspective).
    /// </summary>
    ValueTask<ImmutableArray<ReferenceAssemblyResult>> FindReferenceAssembliesAsync(
        TypeQuery typeQuery, NamespaceQuery namespaceQuery, CancellationToken cancellationToken);
}
 
[DataContract]
internal abstract class AbstractPackageResult(string packageName, int rank)
{
    [DataMember(Order = 0)]
    public readonly string PackageName = packageName;
 
    [DataMember(Order = 1)]
    public readonly int Rank = rank;
}
 
[DataContract]
internal sealed class PackageResult(
    string packageName,
    int rank,
    string typeName,
    string? version,
    ImmutableArray<string> containingNamespaceNames) : AbstractPackageResult(packageName, rank)
{
    [DataMember(Order = 2)]
    public readonly string TypeName = typeName;
 
    [DataMember(Order = 3)]
    public readonly string? Version = version;
 
    [DataMember(Order = 4)]
    public readonly ImmutableArray<string> ContainingNamespaceNames = containingNamespaceNames;
}
 
[DataContract]
internal sealed class PackageWithAssemblyResult(
    string packageName,
    int rank,
    string version) : AbstractPackageResult(packageName, rank), IEquatable<PackageWithAssemblyResult?>, IComparable<PackageWithAssemblyResult?>
{
    [DataMember(Order = 2)]
    public readonly string? Version = version;
 
    public override int GetHashCode()
        => PackageName.GetHashCode();
 
    public override bool Equals(object? obj)
        => Equals(obj as PackageWithAssemblyResult);
 
    public bool Equals(PackageWithAssemblyResult? other)
        => PackageName.Equals(other?.PackageName);
 
    public int CompareTo(PackageWithAssemblyResult? other)
    {
        if (other is null)
            return 1;
 
        return ComparerWithState.CompareTo(this, other, s_comparers);
    }
 
    private static readonly ImmutableArray<Func<PackageWithAssemblyResult, IComparable>> s_comparers =
        [p => p.Rank, p => p.PackageName];
}
 
[DataContract]
internal sealed class ReferenceAssemblyResult(
    string assemblyName,
    string typeName,
    ImmutableArray<string> containingNamespaceNames)
{
    [DataMember(Order = 0)]
    public readonly string AssemblyName = assemblyName;
 
    [DataMember(Order = 1)]
    public readonly string TypeName = typeName;
 
    [DataMember(Order = 2)]
    public readonly ImmutableArray<string> ContainingNamespaceNames = containingNamespaceNames;
}
 
[ExportWorkspaceService(typeof(ISymbolSearchService)), Shared]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal sealed class DefaultSymbolSearchService() : ISymbolSearchService
{
    public ValueTask<ImmutableArray<PackageResult>> FindPackagesAsync(string source, TypeQuery typeQuery, NamespaceQuery namespaceQuery, CancellationToken cancellationToken)
        => ValueTaskFactory.FromResult(ImmutableArray<PackageResult>.Empty);
 
    public ValueTask<ImmutableArray<PackageWithAssemblyResult>> FindPackagesWithAssemblyAsync(string source, string assemblyName, CancellationToken cancellationToken)
        => ValueTaskFactory.FromResult(ImmutableArray<PackageWithAssemblyResult>.Empty);
 
    public ValueTask<ImmutableArray<ReferenceAssemblyResult>> FindReferenceAssembliesAsync(TypeQuery typeQuery, NamespaceQuery namespaceQuery, CancellationToken cancellationToken)
        => ValueTaskFactory.FromResult(ImmutableArray<ReferenceAssemblyResult>.Empty);
}