private static void BuildReplaceMapForConditionalDirectives(UsingsHelper usingsHelper, Dictionary <UsingDirectiveSyntax, UsingDirectiveSyntax> replaceMap, IndentationOptions indentationOptions, DirectiveSpan rootSpan)
        {
            foreach (var childSpan in rootSpan.Children)
            {
                var originalUsings = usingsHelper.GetContainedUsings(childSpan);
                if (originalUsings.Count > 0)
                {
                    // sort the original using declarations on Span.Start, in order to have the correct replace mapping.
                    originalUsings.Sort(CompareSpanStart);

                    var indentationSteps = IndentationHelper.GetIndentationSteps(indentationOptions, originalUsings[0].Parent);
                    if (originalUsings[0].Parent is NamespaceDeclarationSyntax)
                    {
                        indentationSteps++;
                    }

                    var indentation = IndentationHelper.GenerateIndentationString(indentationOptions, indentationSteps);

                    var modifiedUsings = usingsHelper.GenerateGroupedUsings(childSpan, indentation, false, qualifyNames: false);

                    for (var i = 0; i < originalUsings.Count; i++)
                    {
                        replaceMap.Add(originalUsings[i], modifiedUsings[i]);
                    }
                }

                BuildReplaceMapForConditionalDirectives(usingsHelper, replaceMap, indentationOptions, childSpan);
            }
        }
Exemple #2
0
        public ITextOutput Generate(ICodeSetup codeSetup, ICodeGeneratorSetup codeGeneratorSetup, IProject target, IQuantityModel model, ICodeRun run, long index)
        {
            return(new TextOutput($@"
// --------------------------------------------------------------------------------------------------------------------
// <copyright file=""{run.FileName}"" company=""Hukano"">
// Copyright (c) Hukano. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

// <auto-generated />
namespace {NamespaceHelper.CombineNamespaces(target.RootNamespace, run.Namespace)}
{{
{UsingsHelper.GetUsings(codeSetup.UseGlobalUsings | codeGeneratorSetup.UseGlobalUsings, 4, codeSetup.Usings, codeGeneratorSetup.Usings)}

    /// <summary>
    /// Interface for <see cref=""{model.Name}""/> unit selector.
    /// </summary>
    [GeneratedCode(""{this.GetType().FullName}"", ""{this.GetType().Assembly.GetName().Version}"")]
    public interface I{model.Name}UnitSelector : IUnitSelector
    {{
{this.GetUnits(model)}
    }}
}}
"));
        }
        private static void BuildReplaceMapForNamespaces(UsingsHelper usingsHelper, Dictionary <UsingDirectiveSyntax, UsingDirectiveSyntax> replaceMap, IndentationOptions indentationOptions, bool qualifyNames)
        {
            var usingsPerNamespace = usingsHelper
                                     .GetContainedUsings(usingsHelper.RootSpan)
                                     .GroupBy(ud => ud.Parent)
                                     .Select(gr => gr.ToList());

            foreach (var usingList in usingsPerNamespace)
            {
                if (usingList.Count > 0)
                {
                    // sort the original using declarations on Span.Start, in order to have the correct replace mapping.
                    usingList.Sort(CompareSpanStart);

                    var indentationSteps = IndentationHelper.GetIndentationSteps(indentationOptions, usingList[0].Parent);
                    if (usingList[0].Parent is NamespaceDeclarationSyntax)
                    {
                        indentationSteps++;
                    }

                    var indentation = IndentationHelper.GenerateIndentationString(indentationOptions, indentationSteps);

                    var modifiedUsings = usingsHelper.GenerateGroupedUsings(usingList, indentation, false, qualifyNames);

                    for (var i = 0; i < usingList.Count; i++)
                    {
                        replaceMap.Add(usingList[i], modifiedUsings[i]);
                    }
                }
            }
        }
        private static SyntaxNode AddUsingsToNamespace(SyntaxNode newSyntaxRoot, UsingsHelper usingsHelper, string usingsIndentation, bool hasConditionalDirectives)
        {
            var rootNamespace         = ((CompilationUnitSyntax)newSyntaxRoot).Members.OfType <NamespaceDeclarationSyntax>().First();
            var withTrailingBlankLine = hasConditionalDirectives || rootNamespace.Members.Any() || rootNamespace.Externs.Any();

            var groupedUsings = usingsHelper.GenerateGroupedUsings(usingsHelper.RootSpan, usingsIndentation, withTrailingBlankLine);

            groupedUsings = groupedUsings.AddRange(rootNamespace.Usings);
            var newRootNamespace = rootNamespace.WithUsings(groupedUsings);

            newSyntaxRoot = newSyntaxRoot.ReplaceNode(rootNamespace, newRootNamespace);
            return(newSyntaxRoot);
        }
        public void GetUsings_Then_ResultShouldBeExpectedResult()
        {
            var          usings1        = new[] { "Microsoft.Windows", "System.Collections", "Microsoft.Windows" };
            var          usings2        = new[] { "Microsoft.Win32", "System" };
            const string expectedResult = @"using System;
using System.Collections;
using Microsoft.Win32;
using Microsoft.Windows;";

            var result = UsingsHelper.GetUsings(false, 0, true, usings1, usings2);

            result.Should().Be(expectedResult);
        }
        private static SyntaxNode ReAddFileHeader(SyntaxNode syntaxRoot, SyntaxNode newSyntaxRoot)
        {
            var oldFirstToken = syntaxRoot.GetFirstToken();

            if (!oldFirstToken.HasLeadingTrivia)
            {
                return(newSyntaxRoot);
            }

            var fileHeader = UsingsHelper.GetFileHeader(oldFirstToken.LeadingTrivia.ToList());

            if (!fileHeader.Any())
            {
                return(newSyntaxRoot);
            }

            var newFirstToken = newSyntaxRoot.GetFirstToken();

            return(newSyntaxRoot.ReplaceToken(newFirstToken, newFirstToken.WithLeadingTrivia(fileHeader)));
        }
        private static async Task<Document> GetTransformedDocumentAsync(Document document, SyntaxNode syntaxRoot, CancellationToken cancellationToken)
        {
            var fileHeader = GetFileHeader(syntaxRoot);
            var compilationUnit = (CompilationUnitSyntax)syntaxRoot;

            var settings = SettingsHelper.GetStyleCopSettings(document.Project.AnalyzerOptions, cancellationToken);
            var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            var usingsHelper = new UsingsHelper(settings, semanticModel, compilationUnit, fileHeader);
            var namespaceCount = CountNamespaces(compilationUnit.Members);

            // Only move using declarations inside the namespace when
            // - There are no global attributes
            // - There is only a single namespace declared at the top level
            // - OrderingSettings.UsingDirectivesPlacement is set to InsideNamespace
            UsingDirectivesPlacement usingDirectivesPlacement;

            switch (settings.OrderingRules.UsingDirectivesPlacement)
            {
            case UsingDirectivesPlacement.InsideNamespace:
                if (compilationUnit.AttributeLists.Any()
                    || compilationUnit.Members.Count > 1
                    || namespaceCount > 1)
                {
                    // Override the user's setting with a more conservative one
                    usingDirectivesPlacement = UsingDirectivesPlacement.Preserve;
                }
                else if (namespaceCount == 0)
                {
                    usingDirectivesPlacement = UsingDirectivesPlacement.OutsideNamespace;
                }
                else
                {
                    usingDirectivesPlacement = UsingDirectivesPlacement.InsideNamespace;
                }

                break;

            case UsingDirectivesPlacement.OutsideNamespace:
                usingDirectivesPlacement = UsingDirectivesPlacement.OutsideNamespace;
                break;

            case UsingDirectivesPlacement.Preserve:
            default:
                usingDirectivesPlacement = UsingDirectivesPlacement.Preserve;
                break;
            }

            string usingsIndentation;

            if (usingDirectivesPlacement == UsingDirectivesPlacement.InsideNamespace)
            {
                var rootNamespace = compilationUnit.Members.OfType<NamespaceDeclarationSyntax>().First();
                var indentationLevel = IndentationHelper.GetIndentationSteps(settings.Indentation, rootNamespace);
                usingsIndentation = IndentationHelper.GenerateIndentationString(settings.Indentation, indentationLevel + 1);
            }
            else
            {
                usingsIndentation = string.Empty;
            }

            // - The strategy is to strip all using directive that are not inside a conditional directive and replace them later with a sorted list at the correct spot
            // - The using directives that are inside a conditional directive are replaced (in sorted order) on the spot.
            // - Conditional directives are not moved, as correctly parsing them is too tricky
            // - No using directives will be stripped when there are multiple namespaces. In that case everything is replaced on the spot.
            List<UsingDirectiveSyntax> stripList;
            var replaceMap = new Dictionary<UsingDirectiveSyntax, UsingDirectiveSyntax>();

            // When there are multiple namespaces, do not move using statements outside of them, only sort.
            if (usingDirectivesPlacement == UsingDirectivesPlacement.Preserve)
            {
                BuildReplaceMapForNamespaces(usingsHelper, replaceMap, settings.Indentation, false);
                stripList = new List<UsingDirectiveSyntax>();
            }
            else
            {
                stripList = usingsHelper.GetContainedUsings(usingsHelper.RootSpan);
            }

            BuildReplaceMapForConditionalDirectives(usingsHelper, replaceMap, settings.Indentation, usingsHelper.RootSpan);

            var usingSyntaxRewriter = new UsingSyntaxRewriter(stripList, replaceMap, fileHeader);
            var newSyntaxRoot = usingSyntaxRewriter.Visit(syntaxRoot);

            if (usingDirectivesPlacement == UsingDirectivesPlacement.InsideNamespace)
            {
                newSyntaxRoot = AddUsingsToNamespace(newSyntaxRoot, usingsHelper, usingsIndentation, replaceMap.Any());
            }
            else if (usingDirectivesPlacement == UsingDirectivesPlacement.OutsideNamespace)
            {
                newSyntaxRoot = AddUsingsToCompilationRoot(newSyntaxRoot, usingsHelper, usingsIndentation, replaceMap.Any());
            }

            // Final cleanup
            newSyntaxRoot = StripMultipleBlankLines(newSyntaxRoot);
            newSyntaxRoot = ReAddFileHeader(newSyntaxRoot, fileHeader);

            var newDocument = document.WithSyntaxRoot(newSyntaxRoot.WithoutFormatting());

            return newDocument;
        }
        private static SyntaxNode AddUsingsToCompilationRoot(SyntaxNode newSyntaxRoot, UsingsHelper usingsHelper, string usingsIndentation, bool hasConditionalDirectives)
        {
            var newCompilationUnit = (CompilationUnitSyntax)newSyntaxRoot;
            var withTrailingBlankLine = hasConditionalDirectives || newCompilationUnit.AttributeLists.Any() || newCompilationUnit.Members.Any() || newCompilationUnit.Externs.Any();

            var groupedUsings = usingsHelper.GenerateGroupedUsings(usingsHelper.RootSpan, usingsIndentation, withTrailingBlankLine, qualifyNames: true);
            groupedUsings = groupedUsings.AddRange(newCompilationUnit.Usings);
            newSyntaxRoot = newCompilationUnit.WithUsings(groupedUsings);

            return newSyntaxRoot;
        }
        private static SyntaxNode AddUsingsToNamespace(SyntaxNode newSyntaxRoot, UsingsHelper usingsHelper, string usingsIndentation, bool hasConditionalDirectives)
        {
            var rootNamespace = ((CompilationUnitSyntax)newSyntaxRoot).Members.OfType<NamespaceDeclarationSyntax>().First();
            var withTrailingBlankLine = hasConditionalDirectives || rootNamespace.Members.Any() || rootNamespace.Externs.Any();

            var groupedUsings = usingsHelper.GenerateGroupedUsings(usingsHelper.RootSpan, usingsIndentation, withTrailingBlankLine, qualifyNames: false);
            groupedUsings = groupedUsings.AddRange(rootNamespace.Usings);

            var newRootNamespace = rootNamespace.WithUsings(groupedUsings);
            newSyntaxRoot = newSyntaxRoot.ReplaceNode(rootNamespace, newRootNamespace);

            return newSyntaxRoot;
        }
        private static void BuildReplaceMapForConditionalDirectives(UsingsHelper usingsHelper, Dictionary<UsingDirectiveSyntax, UsingDirectiveSyntax> replaceMap, IndentationSettings indentationSettings, DirectiveSpan rootSpan)
        {
            foreach (var childSpan in rootSpan.Children)
            {
                var originalUsings = usingsHelper.GetContainedUsings(childSpan);
                if (originalUsings.Count > 0)
                {
                    // sort the original using declarations on Span.Start, in order to have the correct replace mapping.
                    originalUsings.Sort(CompareSpanStart);

                    var indentationSteps = IndentationHelper.GetIndentationSteps(indentationSettings, originalUsings[0].Parent);
                    if (originalUsings[0].Parent is NamespaceDeclarationSyntax)
                    {
                        indentationSteps++;
                    }

                    var indentation = IndentationHelper.GenerateIndentationString(indentationSettings, indentationSteps);

                    var modifiedUsings = usingsHelper.GenerateGroupedUsings(childSpan, indentation, false, qualifyNames: false);

                    for (var i = 0; i < originalUsings.Count; i++)
                    {
                        replaceMap.Add(originalUsings[i], modifiedUsings[i]);
                    }
                }

                BuildReplaceMapForConditionalDirectives(usingsHelper, replaceMap, indentationSettings, childSpan);
            }
        }
        public ITextOutput Generate(ICodeSetup codeSetup, ICodeGeneratorSetup codeGeneratorSetup, IProject target, IQuantityModel model, ICodeRun run, long index)
        {
            return(new TextOutput(
                       $@"
// --------------------------------------------------------------------------------------------------------------------
// <copyright file=""{run.FileName}"" company=""Hukano"">
// Copyright (c) Hukano. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

// <auto-generated />
namespace {NamespaceHelper.CombineNamespaces(target.RootNamespace, run.Namespace)}
{{
{UsingsHelper.GetUsings(codeSetup.UseGlobalUsings || codeGeneratorSetup.UseGlobalUsings, 4, codeSetup.Usings, codeGeneratorSetup.Usings)}

    [GeneratedCode(""{this.GetType().FullName}"", ""{this.GetType().Assembly.GetName().Version}"")]
    public partial struct {model.Name} : IQuantity<{model.Name}, {model.Name}UnitSelector>
    {{
        private readonly double value;

{ConstructorsHelper.GetConstructors(model)}

        /// <summary>
        /// Gets the value.
        /// </summary>
        /// <value>
        /// The value.
        /// </value>
        double IQuantity.Value => this.value;

        /// <summary>
        /// Gets the unit.
        /// </summary>
        /// <value>
        /// The unit.
        /// </value>
        public IUnit Unit {{ get; }}

{StandardOperationsHelper.GetStandardOperations(model)}

{ConversionOperationsHelper.GetConversionOperations(model)}

{EqualityOperationsHelper.GetEqualityOperations(model)}

{FormattingOperationsHelper.GetToStringOperations(model)}

        /// <summary>
        /// Creates the quantity.
        /// </summary>
        /// <param name=""value"">The quantity value.</param>
        /// <param name=""unit"">The quantity unit.</param>
        /// <returns>
        /// A <see cref=""{model.Name}"" />.
        /// </returns>
        public {model.Name} CreateQuantity(double value, IUnit unit)
        {{
            return new {model.Name}(value, unit);
        }}

        /// <summary>
        /// Gets the result.
        /// </summary>
        /// <returns>
        /// An <see cref=""IQuantity"" />.
        /// </returns>
        IQuantity IDeferredQuantity.GetResult()
        {{
            return this;
        }}

        /// <summary>
        /// Creates the unit selector.
        /// </summary>
        /// <returns>
        /// A unit selector.
        /// </returns>
        {model.Name}UnitSelector IQuantity<{model.Name}, {model.Name}UnitSelector>.CreateUnitSelector()
        {{
            return new {model.Name}UnitSelector();
        }}
    }}
}}
"));
        }
Exemple #12
0
        public ITextOutput Generate(ICodeSetup codeSetup, ICodeGeneratorSetup codeGeneratorSetup, IProject target, IQuantityModel model, ICodeRun run, long index)
        {
            return(new TextOutput(new IndentedString($@"
// --------------------------------------------------------------------------------------------------------------------
// <copyright file=""{run.FileName}"" company=""Hukano"">
// Copyright (c) Hukano. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

// <auto-generated />
namespace {NamespaceHelper.CombineNamespaces(target.RootNamespace, run.Namespace)}
{{
{UsingsHelper.GetUsings(codeSetup.UseGlobalUsings | codeGeneratorSetup.UseGlobalUsings, 4, codeSetup.Usings, codeGeneratorSetup.Usings)}

    /// <summary>
    /// UnitSelector for <see cref=""{model.Name}""/>.
    /// </summary>
    [GeneratedCode(""{this.GetType().FullName}"", ""{this.GetType().Assembly.GetName().Version}"")]
    public class {model.Name}UnitSelector : IExponentSelector<{PrefixesHelper.GetPrefixesAndUnitsInterface(model)}>, {PrefixesHelper.GetPrefixesAndUnitsInterface(model)}
    {{
        private int actualExponent = 1;

        private Prefix actualPrefix;

        /// <summary>
        /// Gets the base unit.
        /// </summary>
        /// <value>
        /// The base unit.
        /// </value>
        public Expression BaseUnit => UnitDefinitions.{model.BaseUnit};

        /// <summary>
        /// Gets the default units.
        /// </summary>
        /// <returns>
        /// An <see cref=""IEnumerable{{IUnit}}"" />.
        /// </returns>
        public IEnumerable<IUnit> GetDefaultUnits()
        {{
            {this.GetDefaultUnits(model.DefaultUnits)}
        }}

{ExponentsHelper.GetExponents(model)}

{PrefixesHelper.GetPrefixes(model)}

{UnitsHelper.GetUnits(model)}

        /// <summary>
        /// Specifies the prefix by the specified factor.
        /// </summary>
        /// <param name=""factor"">The factor.</param>
        /// <returns>The available units.</returns>
        public {PrefixesHelper.GetUnitsInterface(model)} By(double factor)
        {{
            return this.GetUnits(new FactoredPrefix(factor));
        }}

        /// <summary>
        /// Specifies the exponent.
        /// </summary>
        /// <param name=""exponent"">The exponent.</param>
        public void SpecifyExponent(int exponent)
        {{
            this.actualExponent = exponent;
        }}

        /// <summary>
        /// Specifies the prefix.
        /// </summary>
        /// <param name=""prefix"">The prefix.</param>
        public void SpecifyPrefix(Prefix prefix)
        {{
            this.actualPrefix = prefix;
        }}

        /// <summary>
        /// Selects the unit based on the specified magnitude, prefix and base unit.
        /// </summary>
        /// <param name=""unit"">The unit.</param>
        /// <returns>
        /// An <see cref=""Expression""/>.
        /// </returns>
        public Expression SpecifyUnit(IUnit unit)
        {{
            return UnitSelectorHelper.CreateExpression(ref this.actualExponent, ref this.actualPrefix, unit);
        }}

        /// <summary>
        /// Gets the prefixes and unit selector.
        /// </summary>
        /// <param name=""exponent"">The exponent.</param>
        /// <returns>
        /// The available prefixes and units.
        /// </returns>
        private {PrefixesHelper.GetPrefixesAndUnitsInterface(model)} GetPrefixesAndUnits(int exponent)
        {{
            this.SpecifyExponent(exponent);
            return this;
        }}

        /// <summary>
        /// Gets the unit selector.
        /// </summary>
        /// <param name=""prefix"">The prefix.</param>
        /// <returns>The available units.</returns>
        private {PrefixesHelper.GetUnitsInterface(model)} GetUnits(Prefix prefix)
        {{
            this.SpecifyPrefix(prefix);
            return this;
        }}
    }}
}}
")));
        }
Exemple #13
0
        private static async Task ParseCsharpFiles(string solutionFullPath, bool previewOnly, string tfsExePath)
        {
            foreach (var item in await GetUnitTestItems(solutionFullPath))
            {
                var stub2Moq    = new MsTestStub2Moq(item.Workspace, item.File, item.SemanticModel);
                var rewriter    = new MsTestStubRewriter(stub2Moq);
                var newTestFile = rewriter.Visit(item.Root);

                if (newTestFile != item.Root)
                {
                    var root = newTestFile.SyntaxTree.GetRoot();
                    var compilationUnitSyntax = root as CompilationUnitSyntax;

                    if (compilationUnitSyntax != null)
                    {
                        bool hasMoqUsing = compilationUnitSyntax.Usings
                                           .Select(u => u.Name)
                                           .OfType <IdentifierNameSyntax>()
                                           .Any(u => u.Identifier.ValueText.Equals("Moq", StringComparison.InvariantCulture));

                        if (!hasMoqUsing)
                        {
                            var moq      = SyntaxFactory.ParseName("Moq");
                            var moqUsing = SyntaxFactory.UsingDirective(moq).NormalizeWhitespace().WithTrailingTrivia(SyntaxFactory.CarriageReturnLineFeed);
                            root = compilationUnitSyntax.AddUsings(moqUsing);
                        }
                    }

                    var newDoc           = item.File.WithSyntaxRoot(root);
                    var newSemanticModel = await newDoc.GetSemanticModelAsync().ConfigureAwait(false);

                    root = await newDoc.GetSyntaxRootAsync();

                    // remove useless usings (.Fakes and others)
                    root = UsingsHelper.RemoveUnusedImportDirectives(newSemanticModel, root, CancellationToken.None, (r, usingNode) =>
                    {
                        if (usingNode.ToString().Contains("Moq"))
                        {
                            return(!r.ToString().Contains("Mock"));
                        }
                        else
                        {
                            return(true);
                        }
                    });

                    if (previewOnly)
                    {
                        Console.WriteLine(newTestFile.ToFullString());
                    }
                    else
                    {
                        if (!string.IsNullOrEmpty(tfsExePath))
                        {
                            TfsCheckOut(tfsExePath, item.File.FilePath);
                        }

                        using (var writer = new StreamWriter(item.File.FilePath, false, System.Text.UTF8Encoding.UTF8))
                        {
                            root.WriteTo(writer);
                        }
                    }
                }
            }
        }
        private static Task<Document> GetTransformedDocumentAsync(Document document, SyntaxNode syntaxRoot, CancellationToken cancellationToken)
        {
            var compilationUnit = (CompilationUnitSyntax)syntaxRoot;

            var indentationOptions = IndentationOptions.FromDocument(document);

            var usingsHelper = new UsingsHelper(document, compilationUnit);
            var namespaceCount = CountNamespaces(compilationUnit.Members);

            // Only move using declarations inside the namespace when
            // - SA1200 is enabled
            // - There are no global attributes
            // - There is only a single namespace declared at the top level
            var moveInsideNamespace =
                !document.Project.CompilationOptions.IsAnalyzerSuppressed(SA1200UsingDirectivesMustBePlacedWithinNamespace.DiagnosticId)
                && !compilationUnit.AttributeLists.Any()
                && compilationUnit.Members.Count == 1
                && namespaceCount == 1;

            string usingsIndentation;

            if (moveInsideNamespace)
            {
                var rootNamespace = compilationUnit.Members.OfType<NamespaceDeclarationSyntax>().First();
                var indentationLevel = IndentationHelper.GetIndentationSteps(indentationOptions, rootNamespace);
                usingsIndentation = IndentationHelper.GenerateIndentationString(indentationOptions, indentationLevel + 1);
            }
            else
            {
                usingsIndentation = string.Empty;
            }

            // - The strategy is to strip all using directive that are not inside a conditional directive and replace them later with a sorted list at the correct spot
            // - The using directives that are inside a conditional directive are replaced (in sorted order) on the spot.
            // - Conditional directives are not moved, as correctly parsing them is too tricky
            // - No using directives will be stripped when there are multiple namespaces. In that case everything is replaced on the spot.
            List<UsingDirectiveSyntax> stripList;
            var replaceMap = new Dictionary<UsingDirectiveSyntax, UsingDirectiveSyntax>();

            // When there are multiple namespaces, do not move using statements outside of them, only sort.
            if (namespaceCount > 1)
            {
                BuildReplaceMapForNamespaces(usingsHelper, replaceMap, indentationOptions);
                stripList = new List<UsingDirectiveSyntax>();
            }
            else
            {
                stripList = usingsHelper.GetContainedUsings(usingsHelper.RootSpan);
            }

            BuildReplaceMapForConditionalDirectives(usingsHelper, replaceMap, indentationOptions, usingsHelper.RootSpan);

            var usingSyntaxRewriter = new UsingSyntaxRewriter(stripList, replaceMap);
            var newSyntaxRoot = usingSyntaxRewriter.Visit(syntaxRoot);

            if (moveInsideNamespace)
            {
                newSyntaxRoot = AddUsingsToNamespace(newSyntaxRoot, usingsHelper, usingsIndentation, replaceMap.Any());
            }
            else if (namespaceCount <= 1)
            {
                newSyntaxRoot = AddUsingsToCompilationRoot(newSyntaxRoot, usingsHelper, usingsIndentation, replaceMap.Any());
            }

            newSyntaxRoot = ReAddFileHeader(syntaxRoot, newSyntaxRoot);

            var newDocument = document.WithSyntaxRoot(newSyntaxRoot.WithoutFormatting());

            return Task.FromResult(newDocument);
        }
        private static Task <Document> GetTransformedDocumentAsync(Document document, SyntaxNode syntaxRoot, CancellationToken cancellationToken)
        {
            var compilationUnit = (CompilationUnitSyntax)syntaxRoot;

            var indentationOptions = IndentationOptions.FromDocument(document);

            var usingsHelper   = new UsingsHelper(document, compilationUnit);
            var namespaceCount = CountNamespaces(compilationUnit.Members);

            // Only move using declarations inside the namespace when
            // - SA1200 is enabled
            // - There are no global attributes
            // - There is only a single namespace declared at the top level
            var moveInsideNamespace =
                !document.Project.CompilationOptions.IsAnalyzerSuppressed(SA1200UsingDirectivesMustBePlacedWithinNamespace.DiagnosticId) &&
                !compilationUnit.AttributeLists.Any() &&
                compilationUnit.Members.Count == 1 &&
                namespaceCount == 1;

            string usingsIndentation;

            if (moveInsideNamespace)
            {
                var rootNamespace    = compilationUnit.Members.OfType <NamespaceDeclarationSyntax>().First();
                var indentationLevel = IndentationHelper.GetIndentationSteps(indentationOptions, rootNamespace);
                usingsIndentation = IndentationHelper.GenerateIndentationString(indentationOptions, indentationLevel + 1);
            }
            else
            {
                usingsIndentation = string.Empty;
            }

            // - The strategy is to strip all using directive that are not inside a conditional directive and replace them later with a sorted list at the correct spot
            // - The using directives that are inside a conditional directive are replaced (in sorted order) on the spot.
            // - Conditional directives are not moved, as correctly parsing them is too tricky
            // - No using directives will be stripped when there are multiple namespaces. In that case everything is replaced on the spot.
            List <UsingDirectiveSyntax> stripList;
            var replaceMap = new Dictionary <UsingDirectiveSyntax, UsingDirectiveSyntax>();

            // When there are multiple namespaces, do not move using statements outside of them, only sort.
            if (namespaceCount > 1)
            {
                BuildReplaceMapForNamespaces(usingsHelper, replaceMap, indentationOptions);
                stripList = new List <UsingDirectiveSyntax>();
            }
            else
            {
                stripList = usingsHelper.GetContainedUsings(usingsHelper.RootSpan);
            }

            BuildReplaceMapForConditionalDirectives(usingsHelper, replaceMap, indentationOptions, usingsHelper.RootSpan);

            var usingSyntaxRewriter = new UsingSyntaxRewriter(stripList, replaceMap);
            var newSyntaxRoot       = usingSyntaxRewriter.Visit(syntaxRoot);

            if (moveInsideNamespace)
            {
                newSyntaxRoot = AddUsingsToNamespace(newSyntaxRoot, usingsHelper, usingsIndentation, replaceMap.Any());
            }
            else if (namespaceCount <= 1)
            {
                newSyntaxRoot = AddUsingsToCompilationRoot(newSyntaxRoot, usingsHelper, usingsIndentation, replaceMap.Any());
            }

            newSyntaxRoot = ReAddFileHeader(syntaxRoot, newSyntaxRoot);

            var newDocument = document.WithSyntaxRoot(newSyntaxRoot.WithoutFormatting());

            return(Task.FromResult(newDocument));
        }
        private static SyntaxNode AddUsingsToCompilationRoot(SyntaxNode newSyntaxRoot, UsingsHelper usingsHelper, string usingsIndentation, bool hasConditionalDirectives)
        {
            var newCompilationUnit    = (CompilationUnitSyntax)newSyntaxRoot;
            var withTrailingBlankLine = hasConditionalDirectives || newCompilationUnit.AttributeLists.Any() || newCompilationUnit.Members.Any() || newCompilationUnit.Externs.Any();

            var groupedUsings = usingsHelper.GenerateGroupedUsings(usingsHelper.RootSpan, usingsIndentation, withTrailingBlankLine, qualifyNames: true);

            groupedUsings = groupedUsings.AddRange(newCompilationUnit.Usings);
            newSyntaxRoot = newCompilationUnit.WithUsings(groupedUsings);

            return(newSyntaxRoot);
        }
        private static async Task <Document> GetTransformedDocumentAsync(Document document, SyntaxNode syntaxRoot, CancellationToken cancellationToken)
        {
            var fileHeader      = GetFileHeader(syntaxRoot);
            var compilationUnit = (CompilationUnitSyntax)syntaxRoot;

            var settings      = SettingsHelper.GetStyleCopSettings(document.Project.AnalyzerOptions, cancellationToken);
            var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            var indentationOptions = IndentationOptions.FromDocument(document);

            var usingsHelper   = new UsingsHelper(settings, semanticModel, compilationUnit, fileHeader);
            var namespaceCount = CountNamespaces(compilationUnit.Members);

            // Only move using declarations inside the namespace when
            // - There are no global attributes
            // - There is only a single namespace declared at the top level
            // - OrderingSettings.UsingDirectivesPlacement is set to InsideNamespace
            UsingDirectivesPlacement usingDirectivesPlacement;

            switch (settings.OrderingRules.UsingDirectivesPlacement)
            {
            case UsingDirectivesPlacement.InsideNamespace:
                if (compilationUnit.AttributeLists.Any() ||
                    compilationUnit.Members.Count > 1 ||
                    namespaceCount > 1)
                {
                    // Override the user's setting with a more conservative one
                    usingDirectivesPlacement = UsingDirectivesPlacement.Preserve;
                }
                else if (namespaceCount == 0)
                {
                    usingDirectivesPlacement = UsingDirectivesPlacement.OutsideNamespace;
                }
                else
                {
                    usingDirectivesPlacement = UsingDirectivesPlacement.InsideNamespace;
                }

                break;

            case UsingDirectivesPlacement.OutsideNamespace:
                usingDirectivesPlacement = UsingDirectivesPlacement.OutsideNamespace;
                break;

            case UsingDirectivesPlacement.Preserve:
            default:
                usingDirectivesPlacement = UsingDirectivesPlacement.Preserve;
                break;
            }

            string usingsIndentation;

            if (usingDirectivesPlacement == UsingDirectivesPlacement.InsideNamespace)
            {
                var rootNamespace    = compilationUnit.Members.OfType <NamespaceDeclarationSyntax>().First();
                var indentationLevel = IndentationHelper.GetIndentationSteps(indentationOptions, rootNamespace);
                usingsIndentation = IndentationHelper.GenerateIndentationString(indentationOptions, indentationLevel + 1);
            }
            else
            {
                usingsIndentation = string.Empty;
            }

            // - The strategy is to strip all using directive that are not inside a conditional directive and replace them later with a sorted list at the correct spot
            // - The using directives that are inside a conditional directive are replaced (in sorted order) on the spot.
            // - Conditional directives are not moved, as correctly parsing them is too tricky
            // - No using directives will be stripped when there are multiple namespaces. In that case everything is replaced on the spot.
            List <UsingDirectiveSyntax> stripList;
            var replaceMap = new Dictionary <UsingDirectiveSyntax, UsingDirectiveSyntax>();

            // When there are multiple namespaces, do not move using statements outside of them, only sort.
            if (usingDirectivesPlacement == UsingDirectivesPlacement.Preserve)
            {
                BuildReplaceMapForNamespaces(usingsHelper, replaceMap, indentationOptions, false);
                stripList = new List <UsingDirectiveSyntax>();
            }
            else
            {
                stripList = usingsHelper.GetContainedUsings(usingsHelper.RootSpan);
            }

            BuildReplaceMapForConditionalDirectives(usingsHelper, replaceMap, indentationOptions, usingsHelper.RootSpan);

            var usingSyntaxRewriter = new UsingSyntaxRewriter(stripList, replaceMap, fileHeader);
            var newSyntaxRoot       = usingSyntaxRewriter.Visit(syntaxRoot);

            if (usingDirectivesPlacement == UsingDirectivesPlacement.InsideNamespace)
            {
                newSyntaxRoot = AddUsingsToNamespace(newSyntaxRoot, usingsHelper, usingsIndentation, replaceMap.Any());
            }
            else if (usingDirectivesPlacement == UsingDirectivesPlacement.OutsideNamespace)
            {
                newSyntaxRoot = AddUsingsToCompilationRoot(newSyntaxRoot, usingsHelper, usingsIndentation, replaceMap.Any());
            }

            // Final cleanup
            newSyntaxRoot = StripMultipleBlankLines(newSyntaxRoot);
            newSyntaxRoot = ReAddFileHeader(newSyntaxRoot, fileHeader);

            var newDocument = document.WithSyntaxRoot(newSyntaxRoot.WithoutFormatting());

            return(newDocument);
        }
        private static void BuildReplaceMapForNamespaces(UsingsHelper usingsHelper, Dictionary<UsingDirectiveSyntax, UsingDirectiveSyntax> replaceMap, IndentationSettings indentationSettings, bool qualifyNames)
        {
            var usingsPerNamespace = usingsHelper
                .GetContainedUsings(usingsHelper.RootSpan)
                .GroupBy(ud => ud.Parent)
                .Select(gr => gr.ToList());

            foreach (var usingList in usingsPerNamespace)
            {
                if (usingList.Count > 0)
                {
                    // sort the original using declarations on Span.Start, in order to have the correct replace mapping.
                    usingList.Sort(CompareSpanStart);

                    var indentationSteps = IndentationHelper.GetIndentationSteps(indentationSettings, usingList[0].Parent);
                    if (usingList[0].Parent is NamespaceDeclarationSyntax)
                    {
                        indentationSteps++;
                    }

                    var indentation = IndentationHelper.GenerateIndentationString(indentationSettings, indentationSteps);

                    var modifiedUsings = usingsHelper.GenerateGroupedUsings(usingList, indentation, false, qualifyNames);

                    for (var i = 0; i < usingList.Count; i++)
                    {
                        replaceMap.Add(usingList[i], modifiedUsings[i]);
                    }
                }
            }
        }