File: src\Workspaces\SharedUtilitiesAndExtensions\Workspace\CSharp\CodeGeneration\NamespaceGenerator.cs
Web Access
Project: src\src\CodeStyle\CSharp\CodeFixes\Microsoft.CodeAnalysis.CSharp.CodeStyle.Fixes.csproj (Microsoft.CodeAnalysis.CSharp.CodeStyle.Fixes)
// 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.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Roslyn.Utilities;
 
using static Microsoft.CodeAnalysis.CodeGeneration.CodeGenerationHelpers;
using static Microsoft.CodeAnalysis.CSharp.CodeGeneration.CSharpCodeGenerationHelpers;
 
namespace Microsoft.CodeAnalysis.CSharp.CodeGeneration;
 
using static SyntaxFactory;
 
internal static class NamespaceGenerator
{
    public static BaseNamespaceDeclarationSyntax AddNamespaceTo(
        ICodeGenerationService service,
        BaseNamespaceDeclarationSyntax destination,
        INamespaceSymbol @namespace,
        CSharpCodeGenerationContextInfo info,
        IList<bool>? availableIndices,
        CancellationToken cancellationToken)
    {
        var declaration = GenerateNamespaceDeclaration(
            service, @namespace,
            CodeGenerationDestination.Namespace,
            info,
            cancellationToken);
        if (declaration is not BaseNamespaceDeclarationSyntax namespaceDeclaration)
            throw new ArgumentException(WorkspaceExtensionsResources.Namespace_can_not_be_added_in_this_destination);
 
        var members = Insert(destination.Members, namespaceDeclaration, info, availableIndices);
        return destination.WithMembers(members);
    }
 
    public static CompilationUnitSyntax AddNamespaceTo(
        ICodeGenerationService service,
        CompilationUnitSyntax destination,
        INamespaceSymbol @namespace,
        CSharpCodeGenerationContextInfo info,
        IList<bool>? availableIndices,
        CancellationToken cancellationToken)
    {
        var declaration = GenerateNamespaceDeclaration(
            service,
            @namespace,
            CodeGenerationDestination.CompilationUnit,
            info,
            cancellationToken);
 
        if (declaration is not BaseNamespaceDeclarationSyntax namespaceDeclaration)
            throw new ArgumentException(WorkspaceExtensionsResources.Namespace_can_not_be_added_in_this_destination);
 
        var members = Insert(destination.Members, namespaceDeclaration, info, availableIndices);
        return destination.WithMembers(members);
    }
 
    internal static SyntaxNode GenerateNamespaceDeclaration(
        ICodeGenerationService service,
        INamespaceSymbol @namespace,
        CodeGenerationDestination destination,
        CSharpCodeGenerationContextInfo info,
        CancellationToken cancellationToken)
    {
        GetNameAndInnermostNamespace(@namespace, info, out var name, out var innermostNamespace);
 
        var declaration = GetDeclarationSyntaxWithoutMembers(
            @namespace, innermostNamespace, name, destination, info);
 
        declaration = info.Context.GenerateMembers
            ? service.AddMembers(declaration, innermostNamespace.GetMembers(), info, cancellationToken)
            : declaration;
 
        return AddFormatterAndCodeGeneratorAnnotationsTo(declaration);
    }
 
    public static SyntaxNode UpdateCompilationUnitOrNamespaceDeclaration(
        ICodeGenerationService service,
        SyntaxNode declaration,
        IList<ISymbol> newMembers,
        CSharpCodeGenerationContextInfo info,
        CancellationToken cancellationToken)
    {
        declaration = RemoveAllMembers(declaration);
        declaration = service.AddMembers(declaration, newMembers, info, cancellationToken);
        return AddFormatterAndCodeGeneratorAnnotationsTo(declaration);
    }
 
    private static SyntaxNode GenerateNamespaceDeclarationWorker(
        string name, INamespaceSymbol innermostNamespace,
        CodeGenerationDestination destination,
        CSharpCodeGenerationContextInfo info)
    {
        var usings = GenerateUsingDirectives(innermostNamespace);
 
        // If they're just generating the empty namespace then make that into compilation unit.
        if (name == string.Empty)
            return CompilationUnit().WithUsings(usings);
 
        if (destination == CodeGenerationDestination.CompilationUnit &&
            info.Options.NamespaceDeclarations.Value == NamespaceDeclarationPreference.FileScoped &&
            info.LanguageVersion >= LanguageVersion.CSharp10)
        {
            return FileScopedNamespaceDeclaration(ParseName(name)).WithUsings(usings);
        }
 
        return NamespaceDeclaration(ParseName(name)).WithUsings(usings);
    }
 
    private static SyntaxNode GetDeclarationSyntaxWithoutMembers(
        INamespaceSymbol @namespace,
        INamespaceSymbol innermostNamespace,
        string name,
        CodeGenerationDestination destination,
        CSharpCodeGenerationContextInfo info)
    {
        var reusableSyntax = GetReuseableSyntaxNodeForSymbol<SyntaxNode>(@namespace, info);
        return reusableSyntax == null
            ? GenerateNamespaceDeclarationWorker(name, innermostNamespace, destination, info)
            : RemoveAllMembers(reusableSyntax);
    }
 
    private static SyntaxNode RemoveAllMembers(SyntaxNode declaration)
        => declaration switch
        {
            CompilationUnitSyntax compilationUnit => compilationUnit.WithMembers(default),
            BaseNamespaceDeclarationSyntax namespaceDeclaration => namespaceDeclaration.WithMembers(default),
            _ => declaration,
        };
 
    private static SyntaxList<UsingDirectiveSyntax> GenerateUsingDirectives(INamespaceSymbol innermostNamespace)
    {
        var usingDirectives =
            CodeGenerationNamespaceInfo.GetImports(innermostNamespace)
                                       .Select(GenerateUsingDirective)
                                       .WhereNotNull()
                                       .ToList();
 
        return [.. usingDirectives];
    }
 
    private static UsingDirectiveSyntax? GenerateUsingDirective(ISymbol symbol)
    {
        if (symbol is IAliasSymbol alias)
        {
            var name = GenerateName(alias.Target);
            if (name != null)
            {
                return UsingDirective(
                    NameEquals(alias.Name.ToIdentifierName()),
                    name);
            }
        }
        else if (symbol is INamespaceOrTypeSymbol namespaceOrType)
        {
            var name = GenerateName(namespaceOrType);
            if (name != null)
            {
                return UsingDirective(name);
            }
        }
 
        return null;
    }
 
    private static NameSyntax GenerateName(INamespaceOrTypeSymbol symbol)
    {
        return symbol is ITypeSymbol type
            ? type.GenerateNameSyntax()
            : ParseName(symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
    }
}