File: SolutionExplorer\ISolutionExplorerSymbolTreeItemProvider.cs
Web Access
Project: src\src\Features\Core\Portable\Microsoft.CodeAnalysis.Features.csproj (Microsoft.CodeAnalysis.Features)
// 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.Text;
using System.Threading;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
 
namespace Microsoft.CodeAnalysis.SolutionExplorer;
 
internal interface ISolutionExplorerSymbolTreeItemProvider : ILanguageService
{
    ImmutableArray<SymbolTreeItemData> GetItems(DocumentId documentId, SyntaxNode declarationNode, CancellationToken cancellationToken);
}
 
internal abstract class AbstractSolutionExplorerSymbolTreeItemProvider<
    TCompilationUnitSyntax,
    TMemberDeclarationSyntax,
    TNamespaceDeclarationSyntax,
    TEnumDeclarationSyntax,
    TTypeDeclarationSyntax>
    : ISolutionExplorerSymbolTreeItemProvider
    where TCompilationUnitSyntax : SyntaxNode
    where TMemberDeclarationSyntax : SyntaxNode
    where TNamespaceDeclarationSyntax : TMemberDeclarationSyntax
    where TEnumDeclarationSyntax : TMemberDeclarationSyntax
{
    protected static void AppendCommaSeparatedList<TArgumentList, TArgument>(
        StringBuilder builder,
        string openBrace,
        string closeBrace,
        TArgumentList? argumentList,
        Func<TArgumentList, SeparatedSyntaxList<TArgument>> getArguments,
        Action<TArgument, StringBuilder> append,
        string separator = ", ")
        where TArgumentList : SyntaxNode
        where TArgument : SyntaxNode
    {
        if (argumentList is null)
            return;
 
        AppendCommaSeparatedList(builder, openBrace, closeBrace, getArguments(argumentList), append, separator);
    }
 
    protected static void AppendCommaSeparatedList<TNode>(
        StringBuilder builder,
        string openBrace,
        string closeBrace,
        SeparatedSyntaxList<TNode> arguments,
        Action<TNode, StringBuilder> append,
        string separator = ", ")
        where TNode : SyntaxNode
    {
        builder.Append(openBrace);
        builder.AppendJoinedValues(separator, arguments, append);
        builder.Append(closeBrace);
    }
 
    protected abstract SyntaxList<TMemberDeclarationSyntax> GetMembers(TCompilationUnitSyntax root);
    protected abstract SyntaxList<TMemberDeclarationSyntax> GetMembers(TNamespaceDeclarationSyntax baseNamespace);
    protected abstract SyntaxList<TMemberDeclarationSyntax> GetMembers(TTypeDeclarationSyntax typeDeclaration);
 
    protected abstract bool TryAddType(DocumentId documentId, TMemberDeclarationSyntax member, ArrayBuilder<SymbolTreeItemData> items, StringBuilder nameBuilder);
    protected abstract void AddMemberDeclaration(DocumentId documentId, TMemberDeclarationSyntax member, ArrayBuilder<SymbolTreeItemData> items, StringBuilder nameBuilder);
    protected abstract void AddEnumDeclarationMembers(DocumentId documentId, TEnumDeclarationSyntax enumDeclaration, ArrayBuilder<SymbolTreeItemData> items, CancellationToken cancellationToken);
 
    public ImmutableArray<SymbolTreeItemData> GetItems(DocumentId documentId, SyntaxNode node, CancellationToken cancellationToken)
    {
        using var _1 = ArrayBuilder<SymbolTreeItemData>.GetInstance(out var items);
        using var _2 = PooledStringBuilder.GetInstance(out var nameBuilder);
 
        switch (node)
        {
            case TCompilationUnitSyntax compilationUnit:
                AddTopLevelTypes(documentId, compilationUnit, items, nameBuilder, cancellationToken);
                break;
 
            case TEnumDeclarationSyntax enumDeclaration:
                AddEnumDeclarationMembers(documentId, enumDeclaration, items, cancellationToken);
                break;
 
            case TTypeDeclarationSyntax typeDeclaration:
                AddTypeDeclarationMembers(typeDeclaration);
                break;
        }
 
        return items.ToImmutableAndClear();
 
        void AddTypeDeclarationMembers(TTypeDeclarationSyntax typeDeclaration)
        {
            foreach (var member in GetMembers(typeDeclaration))
            {
                cancellationToken.ThrowIfCancellationRequested();
 
                if (TryAddType(documentId, member, items, nameBuilder))
                    continue;
 
                AddMemberDeclaration(documentId, member, items, nameBuilder);
            }
        }
    }
 
    private void AddTopLevelTypes(
        DocumentId documentId,
        TCompilationUnitSyntax root,
        ArrayBuilder<SymbolTreeItemData> items,
        StringBuilder nameBuilder,
        CancellationToken cancellationToken)
    {
        foreach (var member in GetMembers(root))
            RecurseIntoMemberDeclaration(member);
 
        return;
 
        void RecurseIntoMemberDeclaration(TMemberDeclarationSyntax member)
        {
            cancellationToken.ThrowIfCancellationRequested();
 
            if (member is TNamespaceDeclarationSyntax baseNamespace)
            {
                foreach (var childMember in GetMembers(baseNamespace))
                    RecurseIntoMemberDeclaration(childMember);
            }
            else
            {
                TryAddType(documentId, member, items, nameBuilder);
            }
        }
    }
}