﻿// 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;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Formatting;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions;
using Microsoft.CodeAnalysis.Editor.UnitTests.Utilities;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Indentation;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Editor.Commanding.Commands;
using Newtonsoft.Json.Linq;
using Roslyn.Test.Utilities;
using Xunit;
using Xunit.Abstractions;

namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Formatting
{
    [Trait(Traits.Feature, Traits.Features.Formatting)]
    public class FormattingEngineTests : CSharpFormattingEngineTestBase
    {
        public FormattingEngineTests(ITestOutputHelper output) : base(output) { }

        private static OptionsCollection SmartIndentButDoNotFormatWhileTyping()
        {
            return new OptionsCollection(LanguageNames.CSharp)
            {
                { IndentationOptionsStorage.SmartIndent, FormattingOptions2.IndentStyle.Smart },
                { AutoFormattingOptionsStorage.FormatOnTyping, false },
                { AutoFormattingOptionsStorage.FormatOnCloseBrace, false },
            };
        }

        [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539682")]
        public void FormatDocumentCommandHandler()
        {
            var code = @"class Program
{
    static void Main(string[] args)
    {
        int x;$$
int y;
    }
}
";

            var expected = @"class Program
{
    static void Main(string[] args)
    {
        int x;$$
        int y;
    }
}
";

            AssertFormatWithView(expected, code);
        }

        [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539682")]
        public void FormatDocumentPasteCommandHandler()
        {
            var code = @"class Program
{
    static void Main(string[] args)
    {
        int x;$$
int y;
    }
}
";

            var expected = @"class Program
{
    static void Main(string[] args)
    {
        int x;$$
        int y;
    }
}
";

            AssertFormatWithPasteOrReturn(expected, code, allowDocumentChanges: true);
        }

        [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/547261")]
        public void FormatDocumentReadOnlyWorkspacePasteCommandHandler()
        {
            var code = @"class Program
{
    static void Main(string[] args)
    {
        int x;$$
int y;
    }
}
";

            var expected = @"class Program
{
    static void Main(string[] args)
    {
        int x;$$
int y;
    }
}
";

            AssertFormatWithPasteOrReturn(expected, code, allowDocumentChanges: false);
        }

        [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/912965")]
        public void DoNotFormatUsingStatementOnReturn()
        {
            var code = @"class Program
{
    static void Main(string[] args)
    {
        using (null)
                using (null)$$
    }
}
";

            var expected = @"class Program
{
    static void Main(string[] args)
    {
        using (null)
                using (null)$$
    }
}
";

            AssertFormatWithPasteOrReturn(expected, code, allowDocumentChanges: true, isPaste: false);
        }

        [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/912965")]
        public void FormatUsingStatementWhenTypingCloseParen()
        {
            var code = @"class Program
{
    static void Main(string[] args)
    {
        using (null)
                using (null)$$
    }
}
";

            var expected = @"class Program
{
    static void Main(string[] args)
    {
        using (null)
        using (null)
    }
}
";

            AssertFormatAfterTypeChar(code, expected);
        }

        [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/912965")]
        public void FormatNotUsingStatementOnReturn()
        {
            var code = @"class Program
{
    static void Main(string[] args)
    {
        using (null)
                for (;;)$$
    }
}
";

            var expected = @"class Program
{
    static void Main(string[] args)
    {
        using (null)
                for (;;)$$
    }
}
";

            AssertFormatWithPasteOrReturn(expected, code, allowDocumentChanges: true, isPaste: false);
        }

        [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/977133")]
        [WpfFact]
        public void DoNotFormatRangeOrFormatTokenOnOpenBraceOnSameLine()
        {
            var code = @"class C
{
    public void M()
    {
        if (true)        {$$
    }
}";
            var expected = @"class C
{
    public void M()
    {
        if (true)        {
    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("https://github.com/dotnet/roslyn/pull/14491")]
        [WpfFact]
        public void DoNotFormatRangeButFormatTokenOnOpenBraceOnNextLine()
        {
            var code = @"class C
{
    public void M()
    {
        if (true)
            {$$
    }
}";
            var expected = @"class C
{
    public void M()
    {
        if (true)
        {
    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1007071")]
        [WpfFact]
        public void FormatPragmaWarningInbetweenDelegateDeclarationStatement()
        {
            var code = @"using System;

class Program
{
    static void Main(string[] args)
    {
        Func <bool> a = delegate ()
#pragma warning disable CA0001
        {
            return true;
        };$$
    }
}";
            var expected = @"using System;

class Program
{
    static void Main(string[] args)
    {
        Func<bool> a = delegate ()
#pragma warning disable CA0001
        {
            return true;
        };
    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/771761")]
        [WpfFact]
        public void FormatHashRegion()
        {
            var code = @"using System;

class Program
{
    static void Main(string[] args)
    {
#region$$
    }
}";
            var expected = @"using System;

class Program
{
    static void Main(string[] args)
    {
        #region
    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/771761")]
        [WpfFact]
        public void FormatHashEndRegion()
        {
            var code = @"using System;

class Program
{
    static void Main(string[] args)
    {
        #region
#endregion$$
    }
}";
            var expected = @"using System;

class Program
{
    static void Main(string[] args)
    {
        #region
        #endregion
    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/987373")]
        [WpfFact]
        public async Task FormatSpansIndividuallyWithoutCollapsing()
        {
            var code = @"class C
{
    public void M()
    {
        [|if(true){}|]
        [|if(true){}|]
        [|if(true){}|]
        [|if(true){}|]
        [|if(true){}|]
        [|if(true){}|]
        [|if(true){}|]
        [|if(true){}|]
        [|if(true){}|]
        [|if(true){}|]
        [|if(true){}|]
        [|if(true){}|]
        [|if(true){}|]
        [|if(true){}|]
        [|if(true){}|]
        [|if(true){}|]
        [|if(true){}|]
        [|if(true){}|]
        [|if(true){}|]
        [|if(true){}|]
        [|if(true){}|]
        [|if(true){}|]
        [|if(true){}|]
        [|if(true){}|]
        [|if(true){}|]
        [|if(true){}|]
        [|if(true){}|]
        [|if(true){}|]
        [|if(true){}|]
        [|if(true){}|]
        [|if(true){}|]
        if(true){}
        [|if(true){}|]
    }
}";
            var expected = @"class C
{
    public void M()
    {
        if (true) { }
        if (true) { }
        if (true) { }
        if (true) { }
        if (true) { }
        if (true) { }
        if (true) { }
        if (true) { }
        if (true) { }
        if (true) { }
        if (true) { }
        if (true) { }
        if (true) { }
        if (true) { }
        if (true) { }
        if (true) { }
        if (true) { }
        if (true) { }
        if (true) { }
        if (true) { }
        if (true) { }
        if (true) { }
        if (true) { }
        if (true) { }
        if (true) { }
        if (true) { }
        if (true) { }
        if (true) { }
        if (true) { }
        if (true) { }
        if (true) { }
        if(true){}
        if (true) { }
    }
}";
            using var workspace = TestWorkspace.CreateCSharp(code);
            var subjectDocument = workspace.Documents.Single();
            var spans = subjectDocument.SelectedSpans;

            var document = workspace.CurrentSolution.Projects.Single().Documents.Single();
            var syntaxRoot = await document.GetSyntaxRootAsync();
            var options = CSharpSyntaxFormattingOptions.Default;
            var node = Formatter.Format(syntaxRoot, spans, workspace.Services.SolutionServices, options, rules: null, CancellationToken.None);
            Assert.Equal(expected, node.ToFullString());
        }

        [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1044118")]
        [WpfFact]
        public void SemicolonInCommentOnLastLineDoesNotFormat()
        {
            var code = @"using System;

class Program
{
    static void Main(string[] args)
        {
        }
}
// ;$$";

            var expected = @"using System;

class Program
{
    static void Main(string[] args)
        {
        }
}
// ;";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/449")]
        [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1077103")]
        [WpfFact]
        public void NoFormattingInsideSingleLineRegularComment_1()
        {
            var code = @"class Program
{
                              //        {$$
                       static void Main(int a, int b)
    {

    }
}";

            var expected = @"class Program
{
                              //        {
                       static void Main(int a, int b)
    {

    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/449")]
        [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1077103")]
        [WpfFact]
        public void NoFormattingInsideSingleLineRegularComment_2()
        {
            var code = @"class Program
{
                              //        {$$   
                       static void Main(int a, int b)
    {

    }
}";

            var expected = @"class Program
{
                              //        {   
                       static void Main(int a, int b)
    {

    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/449")]
        [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1077103")]
        [WpfFact]
        public void NoFormattingInsideMultiLineRegularComment_1()
        {
            var code = @"class Program
{
    static void Main(int          a/*         {$$       */, int b)
    {

    }
}";

            var expected = @"class Program
{
    static void Main(int          a/*         {       */, int b)
    {

    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/449")]
        [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1077103")]
        [WpfFact]
        public void NoFormattingInsideMultiLineRegularComment_2()
        {
            var code = @"class Program
{
    static void Main(int          a/*         {$$
        */, int b)
    {

    }
}";

            var expected = @"class Program
{
    static void Main(int          a/*         {
        */, int b)
    {

    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/449")]
        [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1077103")]
        [WpfFact]
        public void NoFormattingInsideMultiLineRegularComment_3()
        {
            var code = @"class Program
{
    static void Main(int          a/*         {$$    
        */, int b)
    {

    }
}";

            var expected = @"class Program
{
    static void Main(int          a/*         {    
        */, int b)
    {

    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/449")]
        [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1077103")]
        [WpfFact]
        public void NoFormattingInsideSingleLineDocComment_1()
        {
            var code = @"class Program
{
                              ///        {$$
                       static void Main(int a, int b)
    {

    }
}";

            var expected = @"class Program
{
                              ///        {
                       static void Main(int a, int b)
    {

    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/449")]
        [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1077103")]
        [WpfFact]
        public void NoFormattingInsideSingleLineDocComment_2()
        {
            var code = @"class Program
{
                              ///        {$$   
                       static void Main(int a, int b)
    {

    }
}";

            var expected = @"class Program
{
                              ///        {   
                       static void Main(int a, int b)
    {

    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/449")]
        [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1077103")]
        [WpfFact]
        public void NoFormattingInsideMultiLineDocComment_1()
        {
            var code = @"class Program
{
                              /**        {$$   **/
                       static void Main(int a, int b)
    {

    }
}";

            var expected = @"class Program
{
                              /**        {   **/
                       static void Main(int a, int b)
    {

    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/449")]
        [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1077103")]
        [WpfFact]
        public void NoFormattingInsideMultiLineDocComment_2()
        {
            var code = @"class Program
{
                              /**        {$$   
                **/
                       static void Main(int a, int b)
    {

    }
}";

            var expected = @"class Program
{
                              /**        {   
                **/
                       static void Main(int a, int b)
    {

    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/449")]
        [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1077103")]
        [WpfFact]
        public void NoFormattingInsideMultiLineDocComment_3()
        {
            var code = @"class Program
{
                              /**        {$$
                **/
                       static void Main(int a, int b)
    {

    }
}";

            var expected = @"class Program
{
                              /**        {
                **/
                       static void Main(int a, int b)
    {

    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/449")]
        [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1077103")]
        [WpfFact]
        public void NoFormattingInsideInactiveCode()
        {
            var code = @"class Program
{
                                        #if false
                    {$$
            #endif

    static void Main(string[] args)
    {

    }
}";

            var expected = @"class Program
{
                                        #if false
                    {
            #endif

    static void Main(string[] args)
    {

    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/449")]
        [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1077103")]
        [WpfFact]
        public void NoFormattingInsideStringLiteral()
        {
            var code = @"class Program
{
    static void Main(string[] args)
    {
        var asdas =     ""{$$""        ;
    }
}";

            var expected = @"class Program
{
    static void Main(string[] args)
    {
        var asdas =     ""{""        ;
    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/449")]
        [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1077103")]
        [WpfFact]
        public void NoFormattingInsideCharLiteral()
        {
            var code = @"class Program
{
    static void Main(string[] args)
    {
        var asdas =     '{$$'        ;
    }
}";

            var expected = @"class Program
{
    static void Main(string[] args)
    {
        var asdas =     '{'        ;
    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/449")]
        [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1077103")]
        [WpfFact]
        public void NoFormattingInsideCommentsOfPreprocessorDirectives()
        {
            var code = @"class Program
{
       #region
        #endregion // a/*{$$*/    
        static void Main(string[] args)
    {
        
    }
}";

            var expected = @"class Program
{
       #region
        #endregion // a/*{*/    
        static void Main(string[] args)
    {
        
    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/464")]
        [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/908729")]
        [WpfFact]
        public void ColonInSwitchCase()
        {
            var code = @"class Program
{
    static void Main(string[] args)
    {
        int f = 0;
        switch(f)
        {
                 case     1     :$$    break;
        }
    }
}";

            var expected = @"class Program
{
    static void Main(string[] args)
    {
        int f = 0;
        switch(f)
        {
            case 1:    break;
        }
    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/464")]
        [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/908729")]
        [WpfFact]
        public void ColonInDefaultSwitchCase()
        {
            var code = @"class Program
{
    static void Main(string[] args)
    {
        int f = 0;
        switch(f)
        {
            case 1:    break;
                    default    :$$     break;
        }
    }
}";

            var expected = @"class Program
{
    static void Main(string[] args)
    {
        int f = 0;
        switch(f)
        {
            case 1:    break;
            default:     break;
        }
    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/9097")]
        [WpfFact]
        public void ColonInPatternSwitchCase01()
        {
            var code = @"class Program
{
    static void Main()
    {
        switch(f)
        {
                          case  int  i            :$$    break;
        }
    }
}";

            var expected = @"class Program
{
    static void Main()
    {
        switch(f)
        {
            case int i:    break;
        }
    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/464")]
        [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/908729")]
        [WpfFact]
        public void ColonInLabeledStatement()
        {
            var code = @"class Program
{
    static void Main(string[] args)
    {
            label1   :$$   int s = 0;
    }
}";

            var expected = @"class Program
{
    static void Main(string[] args)
    {
            label1:   int s = 0;
    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/464")]
        [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/908729")]
        [WpfFact]
        public void DoNotFormatColonInTargetAttribute()
        {
            var code = @"using System;
[method    :$$    C]
class C : Attribute
{
}";

            var expected = @"using System;
[method    :    C]
class C : Attribute
{
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/464")]
        [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/908729")]
        [WpfFact]
        public void DoNotFormatColonInBaseList()
        {
            var code = @"class C   :$$   Attribute
{
}";

            var expected = @"class C   :   Attribute
{
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/464")]
        [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/908729")]
        [WpfFact]
        public void DoNotFormatColonInThisConstructor()
        {
            var code = @"class Goo
{
    Goo(int s)   :$$   this()
    {
    }

    Goo()
    {
    }
}";

            var expected = @"class Goo
{
    Goo(int s)   :   this()
    {
    }

    Goo()
    {
    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/464")]
        [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/908729")]
        [WpfFact]
        public void DoNotFormatColonInConditionalOperator()
        {
            var code = @"class Program
{
    static void Main(string[] args)
    {
        var vari = goo()     ?    true  :$$  false;
    }
}";

            var expected = @"class Program
{
    static void Main(string[] args)
    {
        var vari = goo()     ?    true  :  false;
    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/464")]
        [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/908729")]
        [WpfFact]
        public void DoNotFormatColonInArgument()
        {
            var code = @"class Program
{
    static void Main(string[] args)
    {
        Main(args  :$$  args);
    }
}";

            var expected = @"class Program
{
    static void Main(string[] args)
    {
        Main(args  :  args);
    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/464")]
        [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/908729")]
        [WpfFact]
        public void DoNotFormatColonInTypeParameter()
        {
            var code = @"class Program<T>
{
    class C1<U>
        where   T  :$$  U
    {

    }
}";

            var expected = @"class Program<T>
{
    class C1<U>
        where   T  :  U
    {

    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/2224")]
        [WpfFact]
        public void DoNotSmartFormatBracesOnSmartIndentNone()
        {
            var code = @"class Program<T>
{
    class C1<U>
{$$
}";

            var expected = @"class Program<T>
{
    class C1<U>
{
}";
            var globalOptions = new OptionsCollection(LanguageNames.CSharp)
            {
                { IndentationOptionsStorage.SmartIndent, FormattingOptions2.IndentStyle.None }
            };
            AssertFormatAfterTypeChar(code, expected, globalOptions);
        }

        [WpfFact]
        [Trait(Traits.Feature, Traits.Features.SmartTokenFormatting)]
        public void StillAutoIndentCloseBraceWhenFormatOnCloseBraceIsOff()
        {
            var code = @"namespace N
{
    class C
    {
             // improperly indented code
             int x = 10;
        }$$
}
";

            var expected = @"namespace N
{
    class C
    {
             // improperly indented code
             int x = 10;
    }
}
";

            var globalOptions = new OptionsCollection(LanguageNames.CSharp)
            {
                { AutoFormattingOptionsStorage.FormatOnCloseBrace, false }
            };

            AssertFormatAfterTypeChar(code, expected, globalOptions);
        }

        [WpfFact]
        [Trait(Traits.Feature, Traits.Features.SmartTokenFormatting)]
        public void AutoIndentCloseBraceWhenFormatOnTypingIsOff()
        {
            var code = @"namespace N
{
    class C
    {
             // improperly indented code
             int x = 10;
        }$$
}
";

            var expected = @"namespace N
{
    class C
    {
             // improperly indented code
             int x = 10;
    }
}
";

            var globalOptions = new OptionsCollection(LanguageNames.CSharp)
            {
                { AutoFormattingOptionsStorage.FormatOnTyping, false }
            };

            AssertFormatAfterTypeChar(code, expected, globalOptions);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/5873")]
        [WpfFact, Trait(Traits.Feature, Traits.Features.SmartTokenFormatting)]
        public void KeepTabsInCommentsWhenFormattingIsOff()
        {
            // There are tabs in this test case.  Tools that touch the Roslyn repo should
            // not remove these as we are explicitly testing tab behavior.
            var code =
@"class Program
{
    static void Main()
    {
        return;		/* Comment preceded by tabs */		// This one too
        }$$
}";

            var expected =
@"class Program
{
    static void Main()
    {
        return;		/* Comment preceded by tabs */		// This one too
    }
}";

            var globalOptions = new OptionsCollection(LanguageNames.CSharp)
            {
                { AutoFormattingOptionsStorage.FormatOnTyping, false }
            };

            AssertFormatAfterTypeChar(code, expected, globalOptions);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/5873")]
        [WpfFact, Trait(Traits.Feature, Traits.Features.SmartTokenFormatting)]
        public void DoNotKeepTabsInCommentsWhenFormattingIsOn()
        {
            // There are tabs in this test case.  Tools that touch the Roslyn repo should
            // not remove these as we are explicitly testing tab behavior.
            var code = @"class Program
{
    static void Main()
    {
        return;		/* Comment preceded by tabs */		// This one too
        }$$
}";

            var expected =
@"class Program
{
    static void Main()
    {
        return;     /* Comment preceded by tabs */        // This one too
    }
}";

            AssertFormatAfterTypeChar(code, expected);
        }

        [WpfFact]
        [Trait(Traits.Feature, Traits.Features.SmartTokenFormatting)]
        public void DoNotFormatStatementIfSemicolonOptionIsOff()
        {
            var code =
                @"namespace N
{
    class C
    {
        int x   =   10     ;$$
    }
}
";

            var expected =
@"namespace N
{
    class C
    {
        int x   =   10     ;
    }
}
";

            var globalOptions = new OptionsCollection(LanguageNames.CSharp)
            {
                { AutoFormattingOptionsStorage.FormatOnSemicolon, false }
            };

            AssertFormatAfterTypeChar(code, expected, globalOptions);
        }

        [WpfFact]
        [Trait(Traits.Feature, Traits.Features.SmartTokenFormatting)]
        public void DoNotFormatStatementIfTypingOptionIsOff()
        {
            var code =
                @"namespace N
{
    class C
    {
        int x   =   10     ;$$
    }
}
";

            var expected =
@"namespace N
{
    class C
    {
        int x   =   10     ;
    }
}
";

            var globalOptions = new OptionsCollection(LanguageNames.CSharp)
            {
                { AutoFormattingOptionsStorage.FormatOnTyping, false }
            };

            AssertFormatAfterTypeChar(code, expected, globalOptions);
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/4435")]
        [Trait(Traits.Feature, Traits.Features.SmartTokenFormatting)]
        public void OpenCurlyNotFormattedIfNotAtStartOfLine()
        {
            var code =
@"
class C
{
    public  int     P   {$$
}
";

            var expected =
@"
class C
{
    public  int     P   {
}
";

            AssertFormatAfterTypeChar(code, expected);
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/4435")]
        [Trait(Traits.Feature, Traits.Features.SmartTokenFormatting)]
        public void OpenCurlyFormattedIfAtStartOfLine()
        {
            var code =
@"
class C
{
    public  int     P
        {$$
}
";

            var expected =
@"
class C
{
    public  int     P
    {
}
";

            AssertFormatAfterTypeChar(code, expected);
        }

        [WpfFact]
        public void DoNotFormatIncompleteBlockOnSingleLineIfNotTypingCloseCurly1()
        {
            var code = @"namespace ConsoleApplication1
{
    class Program
    {
        static bool Property
        {
            get { return true;$$
    }
}";
            var expected = @"namespace ConsoleApplication1
{
    class Program
    {
        static bool Property
        {
            get { return true;
    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WpfFact]
        public void DoNotFormatIncompleteBlockOnSingleLineIfNotTypingCloseCurly2()
        {
            var code = @"namespace ConsoleApplication1
{
    class Program
    {
        static bool Property { get { return true;$$
    }
}";
            var expected = @"namespace ConsoleApplication1
{
    class Program
    {
        static bool Property { get { return true;
    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WpfFact]
        public void DoNotFormatIncompleteBlockOnSingleLineIfNotTypingCloseCurly3()
        {
            var code = @"namespace ConsoleApplication1
{
    class Program
    {
        static bool Property { get;$$
    }
}";
            var expected = @"namespace ConsoleApplication1
{
    class Program
    {
        static bool Property { get;
    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WpfFact]
        public void DoNotFormatCompleteBlockOnSingleLineIfTypingCloseCurly1()
        {
            var code = @"namespace ConsoleApplication1
{
    class Program
    {
        static bool Property
        {
            get { return true; }$$
}";
            var expected = @"namespace ConsoleApplication1
{
    class Program
    {
        static bool Property
        {
            get { return true; }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WpfFact]
        public void DoNotFormatCompleteBlockOnSingleLineIfTypingCloseCurly2()
        {
            var code = @"namespace ConsoleApplication1
{
    class Program
    {
        static bool Property { get { return true; }$$
}";
            var expected = @"namespace ConsoleApplication1
{
    class Program
    {
        static bool Property { get { return true; }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WpfFact]
        public void FormatIncompleteBlockOnMultipleLinesIfTypingCloseCurly1()
        {
            var code = @"namespace ConsoleApplication1
{
    class Program
    {
        static bool Property
        {
            get { return true;
    }$$
}";
            var expected = @"namespace ConsoleApplication1
{
    class Program
    {
        static bool Property
        {
            get
            {
                return true;
            }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WpfFact]
        public void FormatIncompleteBlockOnMultipleLinesIfTypingCloseCurly2()
        {
            var code = @"namespace ConsoleApplication1
{
    class Program
    {
        static bool Property
        {
            get { return true;
    }
}$$";
            var expected = @"namespace ConsoleApplication1
{
    class Program
    {
        static bool Property
        {
            get
            {
                return true;
            }
        }";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WpfFact]
        public void DoNotFormatCompleteBlockOnSingleLineIfTypingSemicolon()
        {
            var code =
@"public class Class1
{
    void M()
    {
        try { }
        catch { return;$$
        x.ToString();
    }
}";
            var expected =
@"public class Class1
{
    void M()
    {
        try { }
        catch { return;
        x.ToString();
    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WpfFact]
        public void FormatCompleteBlockOnSingleLineIfTypingCloseCurlyOnLaterLine()
        {
            var code =
@"public class Class1
{
    void M()
    {
        try { }
        catch { return;
        x.ToString();
        }$$
    }
}";
            var expected =
@"public class Class1
{
    void M()
    {
        try { }
        catch
        {
            return;
            x.ToString();
        }
    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/7900")]
        [WpfFact]
        public void FormatLockStatementWithEmbeddedStatementOnSemicolonDifferentLine()
        {
            var code = @"class C
{
    private object _l = new object();
    public void M()
    {
        lock (_l)
                       Console.WriteLine(""d"");$$
    }
}";
            var expected = @"class C
{
    private object _l = new object();
    public void M()
    {
        lock (_l)
            Console.WriteLine(""d"");
    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/7900")]
        [WpfFact]
        public void FormatLockStatementWithEmbeddedStatementOnSemicolonSameLine()
        {
            var code = @"class C
{
    private object _l = new object();
    public void M()
    {
        lock (_l)      Console.WriteLine(""d"");$$
    }
}";
            var expected = @"class C
{
    private object _l = new object();
    public void M()
    {
        lock (_l) Console.WriteLine(""d"");
    }
}";
            AssertFormatAfterTypeChar(code, expected);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/11642")]
        [WpfFact]
        public void FormatArbitraryNodeParenthesizedLambdaExpression()
        {
            // code equivalent to an expression synthesized like so:
            // ParenthesizedExpression(ParenthesizedLambdaExpression(ParameterList(), Block()))
            var code = @"(()=>{})";
            var node = SyntaxFactory.ParseExpression(code);
            var expected = @"(() => { })";
            AssertFormatOnArbitraryNode(node, expected);
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/57465")]
        public async Task FormatLambdaWithDirective()
        {
            var code = @"namespace N
{
    public class C
    {
        protected void Render()
        {
            if (outer)
            {
                    M(() =>
                        {
#nullable enable
                               if (inner)
                                    {
                                    }
                                }
                        );
                }
        }
    }
}
";
            var expected = @"namespace N
{
    public class C
    {
        protected void Render()
        {
            if (outer)
            {
                M(() =>
                    {
#nullable enable
                        if (inner)
                        {
                        }
                    }
                    );
            }
        }
    }
}
";

            await AssertFormatAsync(expected, code, spans: null);
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/57465")]
        public async Task FormatLambdaWithComment()
        {
            var code = @"namespace N
{
    public class C
    {
        protected void Render()
        {
if (true)
            {
            M(() => 
            {
                if (true)
                {
/* marker */
                                    }
                                });
                }
        }
    }
}
";
            var expected = @"namespace N
{
    public class C
    {
        protected void Render()
        {
            if (true)
            {
                M(() =>
                {
                    if (true)
                    {
                        /* marker */
                    }
                });
            }
        }
    }
}
";

            await AssertFormatAsync(expected, code, spans: null);
        }

        [WpfTheory]
        [CombinatorialData]
        [WorkItem("https://github.com/dotnet/roslyn/issues/59637")]
        public async Task FormatAttributeAtEndOfFile(bool trailingNewLine)
        {
            var endOfFile = trailingNewLine ? Environment.NewLine : "";
            var code = $@"using System.Diagnostics.CodeAnalysis;

[assembly:SuppressMessage(""Globalization"", ""CA1308: Normalize strings to uppercase"", Justification = ""My reason"", Scope = ""member"", Target = ""~M:Method"") ] {endOfFile}";
            var expected = $@"using System.Diagnostics.CodeAnalysis;

[assembly: SuppressMessage(""Globalization"", ""CA1308: Normalize strings to uppercase"", Justification = ""My reason"", Scope = ""member"", Target = ""~M:Method"")]{endOfFile}";

            await AssertFormatAsync(expected, code, spans: null);
            await AssertFormatAsync(expected, expected, spans: null);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/30787")]
        [WpfFact]
        public void DoSmartIndentOpenBraceEvenWithFormatWhileTypingOff1()
        {
            var code =
@"class Program
{
    void M()
    {
        if (true)
            {$$
    }
}";

            var expected =
@"class Program
{
    void M()
    {
        if (true)
        {
    }
}";

            AssertFormatAfterTypeChar(code, expected, SmartIndentButDoNotFormatWhileTyping());
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/30787")]
        [WpfFact]
        public void DoSmartIndentOpenBraceEvenWithFormatWhileTypingOff2()
        {
            var code =
@"class Program
{
    void M()
    {
        if (true)
        {}$$
    }
}";

            var expected =
@"class Program
{
    void M()
    {
        if (true)
        { }
    }
}";

            AssertFormatAfterTypeChar(code, expected, SmartIndentButDoNotFormatWhileTyping());
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/30787")]
        [WpfFact]
        public void DoSmartIndentOpenBraceEvenWithFormatWhileTypingOff3()
        {
            // We only smart indent the { if it's on it's own line.
            var code =
@"class Program
{
    void M()
    {
        if (true){$$
    }
}";

            var expected =
@"class Program
{
    void M()
    {
        if (true){
    }
}";

            AssertFormatAfterTypeChar(code, expected, SmartIndentButDoNotFormatWhileTyping());
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/30787")]
        [WpfFact]
        public void DoSmartIndentOpenBraceEvenWithFormatWhileTypingOff4()
        {
            // We only smart indent the { if it's on it's own line.
            var code =
@"class Program
{
    void M()
    {
        if (true){}$$
    }
}";

            var expected =
@"class Program
{
    void M()
    {
        if (true){ }
    }
}";

            AssertFormatAfterTypeChar(code, expected, SmartIndentButDoNotFormatWhileTyping());
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/30787")]
        [WpfFact]
        public void DoSmartIndentOpenBraceEvenWithFormatWhileTypingOff5()
        {
            // Typing the { should not affect the formating of the preceding tokens.
            var code =
@"class Program
{
    void M()
    {
        if ( true )
            {$$
    }
}";

            var expected =
@"class Program
{
    void M()
    {
        if ( true )
        {
    }
}";

            AssertFormatAfterTypeChar(code, expected, SmartIndentButDoNotFormatWhileTyping());
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/30787")]
        [WpfFact]
        public void DoSmartIndentOpenBraceEvenWithFormatWhileTypingOff6()
        {
            // Typing the { should not affect the formating of the preceding tokens.
            var code =
@"class Program
{
    void M()
    {
        if ( true ){$$
    }
}";

            var expected =
@"class Program
{
    void M()
    {
        if ( true ){
    }
}";

            AssertFormatAfterTypeChar(code, expected, SmartIndentButDoNotFormatWhileTyping());
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/30787")]
        [WpfFact]
        public void DoSmartIndentOpenBraceEvenWithFormatWhileTypingOff7()
        {
            var code =
@"class Program
{
    void M()
        {$$
}";

            var expected =
@"class Program
{
    void M()
    {
}";

            AssertFormatAfterTypeChar(code, expected, SmartIndentButDoNotFormatWhileTyping());
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/30787")]
        [WpfFact]
        public void DoSmartIndentCloseBraceEvenWithFormatWhileTypingOff1()
        {
            var code =
@"class Program
{
    void M()
    {
        if (true)
        {
            }$$
    }
}";

            var expected =
@"class Program
{
    void M()
    {
        if (true)
        {
        }
    }
}";

            AssertFormatAfterTypeChar(code, expected, SmartIndentButDoNotFormatWhileTyping());
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/30787")]
        [WpfFact]
        public void DoSmartIndentCloseBraceEvenWithFormatWhileTypingOff2()
        {
            // Note that the { is not updated since we are not formatting.
            var code =
@"class Program
{
    void M()
    {
        if (true) {
            }$$
    }
}";

            var expected =
@"class Program
{
    void M()
    {
        if (true) {
        }
    }
}";

            AssertFormatAfterTypeChar(code, expected, SmartIndentButDoNotFormatWhileTyping());
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/30787")]
        [WpfFact]
        public void DoSmartIndentCloseBraceEvenWithFormatWhileTypingOff3()
        {
            var code =
@"class Program
{
    void M()
    {
        }$$
}";

            var expected =
@"class Program
{
    void M()
    {
    }
}";

            AssertFormatAfterTypeChar(code, expected, SmartIndentButDoNotFormatWhileTyping());
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/30787")]
        [WpfFact]
        public void DoSmartIndentCloseBraceEvenWithFormatWhileTypingOff4()
        {
            // Should not affect formatting of open brace
            var code =
@"class Program
{
    void M() {
        }$$
}";

            var expected =
@"class Program
{
    void M() {
    }
}";

            AssertFormatAfterTypeChar(code, expected, SmartIndentButDoNotFormatWhileTyping());
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/31907")]
        public async Task NullableReferenceTypes()
        {
            var code = @"[|
class MyClass
{
    void MyMethod()
    {
        var returnType = (_useMethodSignatureReturnType ? _methodSignatureOpt !: method).ReturnType;
    }
}
|]";
            var expected = @"
class MyClass
{
    void MyMethod()
    {
        var returnType = (_useMethodSignatureReturnType ? _methodSignatureOpt! : method).ReturnType;
    }
}
";

            await AssertFormatWithBaseIndentAsync(expected, code, baseIndentation: 4);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/30518")]
        [WpfFact]
        public void FormatGeneratedNodeInInitializer()
        {
            var code = @"new bool[] {
    true,
    true
}";

            var expected = @"new bool[] {
    true,
true == false, true
}";

            var tree = SyntaxFactory.ParseSyntaxTree(code, options: TestOptions.Script);
            var root = tree.GetRoot();

            var entry = SyntaxFactory.BinaryExpression(SyntaxKind.EqualsExpression, SyntaxFactory.LiteralExpression(SyntaxKind.TrueLiteralExpression), SyntaxFactory.LiteralExpression(SyntaxKind.FalseLiteralExpression));
            var newRoot = root.InsertNodesBefore(root.DescendantNodes().Last(), new[] { entry });
            AssertFormatOnArbitraryNode(newRoot, expected);
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/27268")]
        public async Task PositionalPattern()
        {
            var code = @"[|
class MyClass
{
    void MyMethod()
    {
        var point = new Point (3, 4);
        if (point is Point (3, 4) _
            && point is Point{x: 3, y: 4} _)
        {
        }
    }
}
|]";
            var expected = @"
class MyClass
{
    void MyMethod()
    {
        var point = new Point(3, 4);
        if (point is Point(3, 4) _
            && point is Point { x: 3, y: 4 } _)
        {
        }
    }
}
";

            await AssertFormatWithBaseIndentAsync(expected, code, baseIndentation: 4);
        }

        [WpfFact]
        public async Task WithExpression()
        {
            var code = @"[|
record C(int Property)
{
    void M()
    {
        _ = this  with  {  Property  =  1  } ;
    }
}
|]";
            var expected = @"
record C(int Property)
{
    void M()
    {
        _ = this with { Property = 1 };
    }
}
";

            await AssertFormatWithBaseIndentAsync(expected, code, baseIndentation: 4);
        }

        [WpfFact]
        public async Task WithExpression_MultiLine()
        {
            var code = @"[|
record C(int Property, int Property2)
{
    void M()
    {
        _ = this  with
{
Property  =  1,
Property2  =  2
} ;
    }
}
|]";
            var expected = @"
record C(int Property, int Property2)
{
    void M()
    {
        _ = this with
        {
            Property = 1,
            Property2 = 2
        };
    }
}
";

            await AssertFormatWithBaseIndentAsync(expected, code, baseIndentation: 4);
        }

        [WpfFact]
        public async Task WithExpression_MultiLine_UserPositionedBraces()
        {
            var code = @"[|
record C(int Property, int Property2)
{
    void M()
    {
        _ = this  with
            {
                Property  =  1,
                Property2  =  2
            } ;
    }
}
|]";
            var expected = @"
record C(int Property, int Property2)
{
    void M()
    {
        _ = this with
        {
            Property = 1,
            Property2 = 2
        };
    }
}
";

            await AssertFormatWithBaseIndentAsync(expected, code, baseIndentation: 4);
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/25003")]
        [WpfFact]
        public void SeparateGroups_KeepMultipleLinesBetweenGroups()
        {
            var code = @"$$
using System.A;
using System.B;


using MS.A;
using MS.B;
";

            var expected = @"$$
using System.A;
using System.B;


using MS.A;
using MS.B;
";

            AssertFormatWithView(expected, code, new OptionsCollection(LanguageNames.CSharp) { { GenerationOptions.SeparateImportDirectiveGroups, true } });
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/25003")]
        [WpfFact]
        public void SeparateGroups_KeepMultipleLinesBetweenGroups_FileScopedNamespace()
        {
            var code = @"$$
namespace N;

using System.A;
using System.B;


using MS.A;
using MS.B;
";

            var expected = @"$$
namespace N;

using System.A;
using System.B;


using MS.A;
using MS.B;
";

            AssertFormatWithView(expected, code, new OptionsCollection(LanguageNames.CSharp) { { GenerationOptions.SeparateImportDirectiveGroups, true } });
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/25003")]
        [WpfFact]
        public void SeparateGroups_DoNotGroupIfNotSorted()
        {
            var code = @"$$
using System.B;
using System.A;
using MS.B;
using MS.A;
";

            var expected = @"$$
using System.B;
using System.A;
using MS.B;
using MS.A;
";

            AssertFormatWithView(expected, code, new OptionsCollection(LanguageNames.CSharp) { { GenerationOptions.SeparateImportDirectiveGroups, true } });
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/25003")]
        [WpfFact]
        public void SeparateGroups_GroupIfSorted()
        {
            var code = @"$$
using System.A;
using System.B;
using MS.A;
using MS.B;
";

            var expected = @"$$
using System.A;
using System.B;

using MS.A;
using MS.B;
";

            AssertFormatWithView(expected, code, new OptionsCollection(LanguageNames.CSharp) { { GenerationOptions.SeparateImportDirectiveGroups, true } });
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/25003")]
        [WpfFact]
        public void SeparateGroups_GroupIfSorted_RecognizeSystemNotFirst()
        {
            var code = @"$$
using MS.A;
using MS.B;
using System.A;
using System.B;
";

            var expected = @"$$
using MS.A;
using MS.B;

using System.A;
using System.B;
";

            AssertFormatWithView(expected, code, new OptionsCollection(LanguageNames.CSharp) { { GenerationOptions.SeparateImportDirectiveGroups, true } });
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/58157")]
        public void FormatImplicitObjectCollection()
        {
            var code = @"class Program
{
    static void Main(string[] args)
    {
        $$List<int> list = new()
        {
            1, 2, 3, 4,
        };
    }
}
";

            var expected = @"class Program
{
    static void Main(string[] args)
    {
        $$List<int> list = new()
        {
            1, 2, 3, 4,
        };
    }
}
";

            AssertFormatWithView(expected, code);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/49492")]
        public void PreserveAnnotationsOnMultiLineTrivia()
        {
            var text = @"
namespace TestApp
{
    class Test
    {
    /* __marker__ */
    }
}
";

            var position = text.IndexOf("/* __marker__ */");
            var syntaxTree = CSharpSyntaxTree.ParseText(text);
            var root = syntaxTree.GetRoot();

            var annotation = new SyntaxAnnotation("marker");
            var markerTrivia = root.FindTrivia(position, findInsideTrivia: true);
            var annotatedMarkerTrivia = markerTrivia.WithAdditionalAnnotations(annotation);
            root = root.ReplaceTrivia(markerTrivia, annotatedMarkerTrivia);

            using var workspace = new AdhocWorkspace();

            var options = CSharpSyntaxFormattingOptions.Default;

            var formattedRoot = Formatter.Format(root, workspace.Services.SolutionServices, options, CancellationToken.None);
            var annotatedTrivia = formattedRoot.GetAnnotatedTrivia("marker");

            Assert.Single(annotatedTrivia);
        }

        [WpfFact]
        public void FormatUserDefinedOperator()
        {
            var code = @"$$
class C
{
    public static C operator + ( C x, C y){
    }
}";

            var expected = @"$$
class C
{
    public static C operator +(C x, C y)
    {
    }
}";

            AssertFormatWithView(expected, code);
        }

        [WpfFact]
        public void FormatUserDefinedUnaryOperator()
        {
            var code = @"$$
class C
{
    public static C operator ++ ( C x){
    }
}";

            var expected = @"$$
class C
{
    public static C operator ++(C x)
    {
    }
}";

            AssertFormatWithView(expected, code);
        }

        [WpfFact]
        public void FormatUserDefinedExplicitCastOperator()
        {
            var code = @"$$
class C
{
    public static explicit operator C ( int x){
    }
}";

            var expected = @"$$
class C
{
    public static explicit operator C(int x)
    {
    }
}";

            AssertFormatWithView(expected, code);
        }

        [WpfFact]
        public void FormatUserDefinedOperatorOnType()
        {
            var code = @"
interface I1
{
    abstract static I1 operator + ( I1 x, I1 y);$$
}";

            var expected = @"
interface I1
{
    abstract static I1 operator +(I1 x, I1 y);
}";

            AssertFormatAfterTypeChar(code, expected);
        }

        [WpfFact]
        public void FormatUserDefinedUnaryOperatorOnType()
        {
            var code = @"
interface I1
{
    abstract static I1 operator ++ ( I1 x);$$
}";

            var expected = @"
interface I1
{
    abstract static I1 operator ++(I1 x);
}";

            AssertFormatAfterTypeChar(code, expected);
        }

        [WpfFact]
        public void FormatUserDefinedExplicitCastOperatorOnType()
        {
            var code = @"
interface I1<T> where T : I1<T>
{
    abstract static explicit operator string ( T x);$$
}";

            var expected = @"
interface I1<T> where T : I1<T>
{
    abstract static explicit operator string(T x);
}";

            AssertFormatAfterTypeChar(code, expected);
        }

        [WpfFact]
        public void FormatUserDefinedCheckedOperator()
        {
            var code = @"$$
class C
{
    public static C operator checked + ( C x, C y){
    }
}";

            var expected = @"$$
class C
{
    public static C operator checked +(C x, C y)
    {
    }
}";

            AssertFormatWithView(expected, code, parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview));
        }

        [WpfFact]
        public void FormatUserDefinedCheckedUnaryOperator()
        {
            var code = @"$$
class C
{
    public static C operator checked ++ ( C x){
    }
}";

            var expected = @"$$
class C
{
    public static C operator checked ++(C x)
    {
    }
}";

            AssertFormatWithView(expected, code, parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview));
        }

        [WpfFact]
        public void FormatUserDefinedExplicitCheckedCastOperator()
        {
            var code = @"$$
class C
{
    public static explicit operator checked C ( int x){
    }
}";

            var expected = @"$$
class C
{
    public static explicit operator checked C(int x)
    {
    }
}";

            AssertFormatWithView(expected, code, parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview));
        }

        [WpfFact]
        public void FormatUserDefinedCheckedOperatorOnType()
        {
            var code = @"
interface I1
{
    abstract static I1 operator checked + ( I1 x, I1 y);$$
}";

            var expected = @"
interface I1
{
    abstract static I1 operator checked +(I1 x, I1 y);
}";

            AssertFormatAfterTypeChar(code, expected, parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview));
        }

        [WpfFact]
        public void FormatUserDefinedCheckedUnaryOperatorOnType()
        {
            var code = @"
interface I1
{
    abstract static I1 operator checked ++ ( I1 x);$$
}";

            var expected = @"
interface I1
{
    abstract static I1 operator checked ++(I1 x);
}";

            AssertFormatAfterTypeChar(code, expected, parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview));
        }

        [WpfFact]
        public void FormatUserDefinedExplicitCheckedCastOperatorOnType()
        {
            var code = @"
interface I1<T> where T : I1<T>
{
    abstract static explicit operator checked string ( T x);$$
}";

            var expected = @"
interface I1<T> where T : I1<T>
{
    abstract static explicit operator checked string(T x);
}";

            AssertFormatAfterTypeChar(code, expected, parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview));
        }

        [WpfFact]
        public void FormatUnsignedRightShift()
        {
            var code = @"$$
class C
{
    public static C operator>>> ( C x, C y){
    }
}";

            var expected = @"$$
class C
{
    public static C operator >>>(C x, C y)
    {
    }
}";

            AssertFormatWithView(expected, code);
        }

        [WpfFact]
        public void FormatCollectionLiteralAfterEquals()
        {
            var code = @"$$
var   v  =   [  1  , 2  , 3  ]  ;";

            var expected = @"$$
var v = [1, 2, 3];";

            AssertFormatWithView(expected, code);
        }

        [WpfFact]
        public void FormatUnsignedRightShiftOnType()
        {
            var code = @"
interface I1
{
    abstract static I1 operator >>> ( I1 x, I1 y);$$
}";

            var expected = @"
interface I1
{
    abstract static I1 operator >>>(I1 x, I1 y);
}";

            AssertFormatAfterTypeChar(code, expected);
        }

        private static void AssertFormatAfterTypeChar(string code, string expected, OptionsCollection globalOptions = null, ParseOptions parseOptions = null)
        {
            using var workspace = TestWorkspace.CreateCSharp(code, parseOptions: parseOptions);

            var subjectDocument = workspace.Documents.Single();

            var commandHandler = workspace.GetService<FormatCommandHandler>();
            var typedChar = subjectDocument.GetTextBuffer().CurrentSnapshot.GetText(subjectDocument.CursorPosition.Value - 1, 1);
            var textView = subjectDocument.GetTextView();

            globalOptions?.SetGlobalOptions(workspace.GlobalOptions);
            workspace.GlobalOptions.SetEditorOptions(textView.Options.GlobalOptions, subjectDocument.Project.Language);

            commandHandler.ExecuteCommand(new TypeCharCommandArgs(textView, subjectDocument.GetTextBuffer(), typedChar[0]), () => { }, TestCommandExecutionContext.Create());

            var newSnapshot = subjectDocument.GetTextBuffer().CurrentSnapshot;

            AssertEx.EqualOrDiff(expected, newSnapshot.GetText());
        }
    }
}
