File: CSharpAddImportsService.cs
Web Access
Project: ..\..\..\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.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, CodeStyleOption2<AddImportPlacement> fallbackValue)
            => configOptions.GetOption(CSharpCodeStyleOptions.PreferredUsingDirectivePlacement, fallbackValue);
 
        // C# doesn't have global imports.
        protected override ImmutableArray<SyntaxNode> GetGlobalImports(Compilation compilation, SyntaxGenerator generator)
            => ImmutableArray<SyntaxNode>.Empty;
 
        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;
            }
        }
    }
}