File: LanguageServices\CSharpSymbolDisplayService.SymbolDescriptionBuilder.cs
Web Access
Project: ..\..\..\src\Features\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.Features.csproj (Microsoft.CodeAnalysis.CSharp.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.
 
#nullable disable
 
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Classification;
using Microsoft.CodeAnalysis.Classification.Classifiers;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.Editor.CSharp.LanguageServices
{
    internal partial class CSharpSymbolDisplayService
    {
        protected class SymbolDescriptionBuilder : AbstractSymbolDescriptionBuilder
        {
            private static readonly SymbolDisplayFormat s_minimallyQualifiedFormat = SymbolDisplayFormat.MinimallyQualifiedFormat
                .AddLocalOptions(SymbolDisplayLocalOptions.IncludeRef)
                .AddMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.UseErrorTypeSymbolName)
                .RemoveParameterOptions(SymbolDisplayParameterOptions.IncludeDefaultValue)
                .WithKindOptions(SymbolDisplayKindOptions.None);
 
            private static readonly SymbolDisplayFormat s_minimallyQualifiedFormatWithConstants = s_minimallyQualifiedFormat
                .AddLocalOptions(SymbolDisplayLocalOptions.IncludeConstantValue)
                .AddMemberOptions(SymbolDisplayMemberOptions.IncludeConstantValue)
                .AddParameterOptions(SymbolDisplayParameterOptions.IncludeDefaultValue);
 
            private static readonly SymbolDisplayFormat s_minimallyQualifiedFormatWithConstantsAndModifiers = s_minimallyQualifiedFormatWithConstants
                .AddMemberOptions(SymbolDisplayMemberOptions.IncludeModifiers);
 
            public SymbolDescriptionBuilder(
                SemanticModel semanticModel,
                int position,
                SolutionServices services,
                IStructuralTypeDisplayService structuralTypeDisplayService,
                SymbolDescriptionOptions options,
                CancellationToken cancellationToken)
                : base(semanticModel, position, services, structuralTypeDisplayService, options, cancellationToken)
            {
            }
 
            protected override void AddDeprecatedPrefix()
            {
                AddToGroup(SymbolDescriptionGroups.MainDescription,
                    Punctuation("["),
                    PlainText(CSharpFeaturesResources.deprecated),
                    Punctuation("]"),
                    Space());
            }
 
            protected override void AddExtensionPrefix()
            {
                AddToGroup(SymbolDescriptionGroups.MainDescription,
                    Punctuation("("),
                    PlainText(CSharpFeaturesResources.extension),
                    Punctuation(")"),
                    Space());
            }
 
            protected override void AddAwaitablePrefix()
            {
                AddToGroup(SymbolDescriptionGroups.MainDescription,
                    Punctuation("("),
                    PlainText(CSharpFeaturesResources.awaitable),
                    Punctuation(")"),
                    Space());
            }
 
            protected override void AddAwaitableExtensionPrefix()
            {
                AddToGroup(SymbolDescriptionGroups.MainDescription,
                    Punctuation("("),
                    PlainText(CSharpFeaturesResources.awaitable_extension),
                    Punctuation(")"),
                    Space());
            }
 
            protected override void AddEnumUnderlyingTypeSeparator()
            {
                AddToGroup(SymbolDescriptionGroups.MainDescription,
                    Space(),
                    Punctuation(":"),
                    Space());
            }
 
            protected override Task<ImmutableArray<SymbolDisplayPart>> GetInitializerSourcePartsAsync(
                ISymbol symbol)
            {
                // Actually check for C# symbol types here.  
                if (symbol is IParameterSymbol parameter)
                {
                    return GetInitializerSourcePartsAsync(parameter);
                }
                else if (symbol is ILocalSymbol local)
                {
                    return GetInitializerSourcePartsAsync(local);
                }
                else if (symbol is IFieldSymbol field)
                {
                    return GetInitializerSourcePartsAsync(field);
                }
 
                return SpecializedTasks.EmptyImmutableArray<SymbolDisplayPart>();
            }
 
            protected override ImmutableArray<SymbolDisplayPart> ToMinimalDisplayParts(ISymbol symbol, SemanticModel semanticModel, int position, SymbolDisplayFormat format)
                => CodeAnalysis.CSharp.SymbolDisplay.ToMinimalDisplayParts(symbol, semanticModel, position, format);
 
            protected override string GetNavigationHint(ISymbol symbol)
                => symbol == null ? null : CodeAnalysis.CSharp.SymbolDisplay.ToDisplayString(symbol, SymbolDisplayFormat.MinimallyQualifiedFormat);
 
            private async Task<ImmutableArray<SymbolDisplayPart>> GetInitializerSourcePartsAsync(
                IFieldSymbol symbol)
            {
                EqualsValueClauseSyntax initializer = null;
 
                var variableDeclarator = await GetFirstDeclarationAsync<VariableDeclaratorSyntax>(symbol).ConfigureAwait(false);
                if (variableDeclarator != null)
                {
                    initializer = variableDeclarator.Initializer;
                }
 
                if (initializer == null)
                {
                    var enumMemberDeclaration = await GetFirstDeclarationAsync<EnumMemberDeclarationSyntax>(symbol).ConfigureAwait(false);
                    if (enumMemberDeclaration != null)
                    {
                        initializer = enumMemberDeclaration.EqualsValue;
                    }
                }
 
                if (initializer != null)
                {
                    return await GetInitializerSourcePartsAsync(initializer).ConfigureAwait(false);
                }
 
                return ImmutableArray<SymbolDisplayPart>.Empty;
            }
 
            private async Task<ImmutableArray<SymbolDisplayPart>> GetInitializerSourcePartsAsync(
                ILocalSymbol symbol)
            {
                var syntax = await GetFirstDeclarationAsync<VariableDeclaratorSyntax>(symbol).ConfigureAwait(false);
                if (syntax != null)
                {
                    return await GetInitializerSourcePartsAsync(syntax.Initializer).ConfigureAwait(false);
                }
 
                return ImmutableArray<SymbolDisplayPart>.Empty;
            }
 
            private async Task<ImmutableArray<SymbolDisplayPart>> GetInitializerSourcePartsAsync(
                IParameterSymbol symbol)
            {
                var syntax = await GetFirstDeclarationAsync<ParameterSyntax>(symbol).ConfigureAwait(false);
                if (syntax != null)
                {
                    return await GetInitializerSourcePartsAsync(syntax.Default).ConfigureAwait(false);
                }
 
                return ImmutableArray<SymbolDisplayPart>.Empty;
            }
 
            private async Task<T> GetFirstDeclarationAsync<T>(ISymbol symbol) where T : SyntaxNode
            {
                foreach (var syntaxRef in symbol.DeclaringSyntaxReferences)
                {
                    var syntax = await syntaxRef.GetSyntaxAsync(CancellationToken).ConfigureAwait(false);
                    if (syntax is T tSyntax)
                    {
                        return tSyntax;
                    }
                }
 
                return null;
            }
 
            private async Task<ImmutableArray<SymbolDisplayPart>> GetInitializerSourcePartsAsync(
                EqualsValueClauseSyntax equalsValue)
            {
                if (equalsValue != null && equalsValue.Value != null)
                {
                    var semanticModel = GetSemanticModel(equalsValue.SyntaxTree);
                    if (semanticModel != null)
                    {
                        return await Classifier.GetClassifiedSymbolDisplayPartsAsync(
                            Services, semanticModel, equalsValue.Value.Span,
                            Options.ClassificationOptions, cancellationToken: CancellationToken).ConfigureAwait(false);
                    }
                }
 
                return ImmutableArray<SymbolDisplayPart>.Empty;
            }
 
            protected override void AddCaptures(ISymbol symbol)
            {
                if (symbol is IMethodSymbol method && method.ContainingSymbol.IsKind(SymbolKind.Method))
                {
                    var syntax = method.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax();
                    if (syntax.IsKind(SyntaxKind.LocalFunctionStatement) || syntax is AnonymousFunctionExpressionSyntax)
                    {
                        AddCaptures(syntax);
                    }
                }
            }
 
            protected override SymbolDisplayFormat MinimallyQualifiedFormat => s_minimallyQualifiedFormat;
 
            protected override SymbolDisplayFormat MinimallyQualifiedFormatWithConstants => s_minimallyQualifiedFormatWithConstants;
 
            protected override SymbolDisplayFormat MinimallyQualifiedFormatWithConstantsAndModifiers => s_minimallyQualifiedFormatWithConstantsAndModifiers;
        }
    }
}