File: src\Workspaces\SharedUtilitiesAndExtensions\Workspace\CSharp\LanguageServices\CSharpAddImportsService.cs
Web Access
Project: src\src\Workspaces\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.Workspaces.csproj (Microsoft.CodeAnalysis.CSharp.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.Diagnostics.CodeAnalysis;
using System.Threading;
using Microsoft.CodeAnalysis.AddImport;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Options;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp.AddImport;
 
[ExportLanguageService(typeof(IAddImportsService), LanguageNames.CSharp), Shared]
internal sealed class CSharpAddImportsService : AbstractAddImportsService<
    CompilationUnitSyntax, BaseNamespaceDeclarationSyntax, UsingDirectiveSyntax, ExternAliasDirectiveSyntax>
{
    [ImportingConstructor]
    [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
    public CSharpAddImportsService()
    {
    }
 
    protected override string Language
        => LanguageNames.CSharp;
 
    public override CodeStyleOption2<AddImportPlacement> GetUsingDirectivePlacementCodeStyleOption(IOptionsReader configOptions)
        => configOptions.GetOption(CSharpCodeStyleOptions.PreferredUsingDirectivePlacement);
 
    // C# doesn't have global imports.
    protected override ImmutableArray<SyntaxNode> GetGlobalImports(Compilation compilation, SyntaxGenerator generator)
        => [];
 
    protected override SyntaxNode? GetAlias(UsingDirectiveSyntax usingOrAlias)
        => usingOrAlias.Alias;
 
    protected override bool IsStaticUsing(UsingDirectiveSyntax usingOrAlias)
        => usingOrAlias.StaticKeyword != default;
 
    protected override SyntaxNode Rewrite(
        ExternAliasDirectiveSyntax[] externAliases,
        UsingDirectiveSyntax[] usingDirectives,
        UsingDirectiveSyntax[] staticUsingDirectives,
        UsingDirectiveSyntax[] aliasDirectives,
        SyntaxNode externContainer,
        SyntaxNode usingContainer,
        SyntaxNode staticUsingContainer,
        SyntaxNode aliasContainer,
        AddImportPlacementOptions options,
        SyntaxNode root,
        CancellationToken cancellationToken)
    {
        var rewriter = new Rewriter(
            externAliases, usingDirectives, staticUsingDirectives, aliasDirectives,
            externContainer, usingContainer, staticUsingContainer, aliasContainer,
            options, cancellationToken);
 
        var newRoot = rewriter.Visit(root);
        return newRoot;
    }
 
    protected override SyntaxList<UsingDirectiveSyntax> GetUsingsAndAliases(SyntaxNode node)
        => node switch
        {
            CompilationUnitSyntax c => c.Usings,
            BaseNamespaceDeclarationSyntax n => n.Usings,
            _ => default,
        };
 
    protected override SyntaxList<ExternAliasDirectiveSyntax> GetExterns(SyntaxNode node)
        => node switch
        {
            CompilationUnitSyntax c => c.Externs,
            BaseNamespaceDeclarationSyntax n => n.Externs,
            _ => default,
        };
 
    protected override bool IsEquivalentImport(SyntaxNode a, SyntaxNode b)
        => SyntaxFactory.AreEquivalent(a, b, kind => kind == SyntaxKind.NullableDirectiveTrivia);
 
    private sealed class Rewriter : CSharpSyntaxRewriter
    {
        private readonly AddImportPlacementOptions _options;
        private readonly CancellationToken _cancellationToken;
        private readonly SyntaxNode _externContainer;
        private readonly SyntaxNode _usingContainer;
        private readonly SyntaxNode _aliasContainer;
        private readonly SyntaxNode _staticUsingContainer;
        private readonly UsingDirectiveSyntax[] _aliasDirectives;
        private readonly ExternAliasDirectiveSyntax[] _externAliases;
        private readonly UsingDirectiveSyntax[] _usingDirectives;
        private readonly UsingDirectiveSyntax[] _staticUsingDirectives;
 
        public Rewriter(
            ExternAliasDirectiveSyntax[] externAliases,
            UsingDirectiveSyntax[] usingDirectives,
            UsingDirectiveSyntax[] staticUsingDirectives,
            UsingDirectiveSyntax[] aliasDirectives,
            SyntaxNode externContainer,
            SyntaxNode usingContainer,
            SyntaxNode aliasContainer,
            SyntaxNode staticUsingContainer,
            AddImportPlacementOptions options,
            CancellationToken cancellationToken)
        {
            _externAliases = externAliases;
            _usingDirectives = usingDirectives;
            _staticUsingDirectives = staticUsingDirectives;
            _aliasDirectives = aliasDirectives;
            _externContainer = externContainer;
            _usingContainer = usingContainer;
            _aliasContainer = aliasContainer;
            _staticUsingContainer = staticUsingContainer;
            _options = options;
            _cancellationToken = cancellationToken;
        }
 
        [return: NotNullIfNotNull(nameof(node))]
        public override SyntaxNode? Visit(SyntaxNode? node)
            => base.Visit(node);
 
        public override SyntaxNode VisitNamespaceDeclaration(NamespaceDeclarationSyntax node)
            => VisitBaseNamespaceDeclaration(node, (BaseNamespaceDeclarationSyntax?)base.VisitNamespaceDeclaration(node));
 
        public override SyntaxNode? VisitFileScopedNamespaceDeclaration(FileScopedNamespaceDeclarationSyntax node)
            => VisitBaseNamespaceDeclaration(node, (BaseNamespaceDeclarationSyntax?)base.VisitFileScopedNamespaceDeclaration(node));
 
        private SyntaxNode VisitBaseNamespaceDeclaration(
            BaseNamespaceDeclarationSyntax node, BaseNamespaceDeclarationSyntax? rewritten)
        {
            Contract.ThrowIfNull(rewritten);
 
            // recurse downwards so we visit inner namespaces first.
            if (!node.CanAddUsingDirectives(_options.AllowInHiddenRegions, _cancellationToken))
            {
                return rewritten;
            }
 
            if (node == _aliasContainer)
            {
                rewritten = rewritten.AddUsingDirectives(_aliasDirectives, _options.PlaceSystemNamespaceFirst);
            }
 
            if (node == _usingContainer)
            {
                rewritten = rewritten.AddUsingDirectives(_usingDirectives, _options.PlaceSystemNamespaceFirst);
            }
 
            if (node == _staticUsingContainer)
            {
                rewritten = rewritten.AddUsingDirectives(_staticUsingDirectives, _options.PlaceSystemNamespaceFirst);
            }
 
            if (node == _externContainer)
            {
                rewritten = rewritten.AddExterns(_externAliases);
            }
 
            return rewritten;
        }
 
        public override SyntaxNode VisitCompilationUnit(CompilationUnitSyntax node)
        {
            // recurse downwards so we visit inner namespaces first.
            var rewritten = (CompilationUnitSyntax)(base.VisitCompilationUnit(node) ?? throw ExceptionUtilities.Unreachable());
 
            if (!node.CanAddUsingDirectives(_options.AllowInHiddenRegions, _cancellationToken))
            {
                return rewritten;
            }
 
            if (node == _aliasContainer)
            {
                rewritten = rewritten.AddUsingDirectives(_aliasDirectives, _options.PlaceSystemNamespaceFirst);
            }
 
            if (node == _usingContainer)
            {
                rewritten = rewritten.AddUsingDirectives(_usingDirectives, _options.PlaceSystemNamespaceFirst);
            }
 
            if (node == _staticUsingContainer)
            {
                rewritten = rewritten.AddUsingDirectives(_staticUsingDirectives, _options.PlaceSystemNamespaceFirst);
            }
 
            if (node == _externContainer)
            {
                rewritten = rewritten.AddExterns(_externAliases);
            }
 
            return rewritten;
        }
    }
}