File: Interactive\BraceMatching\InteractiveBraceHighlightingTests.cs
Web Access
Project: src\src\EditorFeatures\CSharpTest\Microsoft.CodeAnalysis.CSharp.EditorFeatures.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.EditorFeatures.UnitTests)
// 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.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.BraceMatching;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Editor.Tagging;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Tagging;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.BraceHighlighting;
 
[UseExportProvider]
[Trait(Traits.Feature, Traits.Features.BraceHighlighting)]
public class InteractiveBraceHighlightingTests
{
    private static IEnumerable<T> Enumerable<T>(params T[] array)
        => array;
 
    private static async Task<IEnumerable<TagSpan<BraceHighlightTag>>> ProduceTagsAsync(
        EditorTestWorkspace workspace,
        ITextBuffer buffer,
        int position)
    {
        var producer = new BraceHighlightingViewTaggerProvider(
            workspace.GetService<TaggerHost>(),
            workspace.GetService<IBraceMatchingService>());
 
        var context = new TaggerContext<BraceHighlightTag>(
            buffer.CurrentSnapshot.GetRelatedDocumentsWithChanges().FirstOrDefault(),
            buffer.CurrentSnapshot,
            frozenPartialSemantics: false,
            new SnapshotPoint(buffer.CurrentSnapshot, position));
        await producer.GetTestAccessor().ProduceTagsAsync(context);
 
        return context.TagSpans;
    }
 
    [WpfFact]
    public async Task TestCurlies()
    {
        var code = "public class C {\r\n}";
        using var workspace = EditorTestWorkspace.CreateCSharp(code, parseOptions: TestOptions.Script);
        var buffer = workspace.Documents.First().GetTextBuffer();
 
        // Before open curly
        var result = await ProduceTagsAsync(workspace, buffer, 14);
        Assert.True(result.IsEmpty());
 
        // At open curly
        result = await ProduceTagsAsync(workspace, buffer, 15);
        Assert.True(result.Select(ts => ts.Span.Span).SetEquals(Enumerable(Span.FromBounds(15, 16), Span.FromBounds(18, 19))));
 
        // After open curly
        result = await ProduceTagsAsync(workspace, buffer, 16);
        Assert.True(result.IsEmpty());
 
        // At close curly
        result = await ProduceTagsAsync(workspace, buffer, 18);
        Assert.True(result.IsEmpty());
 
        // After close curly
        result = await ProduceTagsAsync(workspace, buffer, 19);
        Assert.True(result.Select(ts => ts.Span.Span).SetEquals(Enumerable(Span.FromBounds(15, 16), Span.FromBounds(18, 19))));
    }
 
    [WpfFact]
    public async Task TestTouchingItems()
    {
        var code = "public class C {\r\n  public void Goo(){}\r\n}";
        using var workspace = EditorTestWorkspace.CreateCSharp(code, TestOptions.Script);
        var buffer = workspace.Documents.First().GetTextBuffer();
 
        // Before open curly
        var result = await ProduceTagsAsync(workspace, buffer, 35);
        Assert.True(result.Select(ts => ts.Span.Span).SetEquals(Enumerable(Span.FromBounds(35, 36), Span.FromBounds(36, 37))));
 
        // At open curly
        result = await ProduceTagsAsync(workspace, buffer, 36);
        Assert.True(result.IsEmpty());
 
        // After open curly
        result = await ProduceTagsAsync(workspace, buffer, 37);
        Assert.True(result.Select(ts => ts.Span.Span).SetEquals(
            Enumerable(Span.FromBounds(35, 36), Span.FromBounds(36, 37), Span.FromBounds(37, 38), Span.FromBounds(38, 39))));
 
        // At close curly
        result = await ProduceTagsAsync(workspace, buffer, 38);
        Assert.True(result.IsEmpty());
 
        // After close curly
        result = await ProduceTagsAsync(workspace, buffer, 39);
        Enumerable(Span.FromBounds(37, 38), Span.FromBounds(38, 39));
    }
 
    [WpfFact]
    public async Task TestAngles()
    {
        var code = "/// <summary>Goo</summary>\r\npublic class C<T> {\r\n  void Goo() {\r\n    bool a = b < c;\r\n    bool d = e > f;\r\n  }\r\n} ";
        using var workspace = EditorTestWorkspace.CreateCSharp(code, parseOptions: TestOptions.Script);
        var buffer = workspace.Documents.First().GetTextBuffer();
 
        // Before open angle of generic
        var result = await ProduceTagsAsync(workspace, buffer, 42);
        Assert.True(result.Select(ts => ts.Span.Span).SetEquals(Enumerable(Span.FromBounds(42, 43), Span.FromBounds(44, 45))));
 
        // After close angle of generic
        result = await ProduceTagsAsync(workspace, buffer, 45);
        Assert.True(result.Select(ts => ts.Span.Span).SetEquals(Enumerable(Span.FromBounds(42, 43), Span.FromBounds(44, 45))));
 
        async Task assertNoTags(int position, char expectedChar)
        {
            Assert.Equal(expectedChar, buffer.CurrentSnapshot[position]);
            result = await ProduceTagsAsync(workspace, buffer, position);
            Assert.True(result.IsEmpty());
            result = await ProduceTagsAsync(workspace, buffer, position + 1);
            Assert.True(result.IsEmpty());
        }
 
        // Doesn't highlight angles of XML doc comments
        var xmlTagStartPosition = 4;
        await assertNoTags(xmlTagStartPosition, '<');
 
        var xmlTagEndPosition = 12;
        await assertNoTags(xmlTagEndPosition, '>');
 
        var xmlEndTagStartPosition = 16;
        await assertNoTags(xmlEndTagStartPosition, '<');
 
        var xmlEndTagEndPosition = 25;
        await assertNoTags(xmlEndTagEndPosition, '>');
 
        // Doesn't highlight operators
        var openAnglePosition = 15 + buffer.CurrentSnapshot.GetLineFromLineNumber(3).Start;
        await assertNoTags(openAnglePosition, '<');
 
        var closeAnglePosition = 15 + buffer.CurrentSnapshot.GetLineFromLineNumber(4).Start;
        await assertNoTags(closeAnglePosition, '>');
    }
 
    [WpfFact]
    public async Task TestSwitch()
    {
        var code = @"
class C
{
    void M(int variable)
    {
        switch (variable)
        {
            case 0:
                break;
        }
    }
} ";
        using var workspace = EditorTestWorkspace.CreateCSharp(code, parseOptions: TestOptions.Script);
        var buffer = workspace.Documents.First().GetTextBuffer();
 
        // At switch open paren
        var result = await ProduceTagsAsync(workspace, buffer, 62);
        AssertEx.Equal(Enumerable(new Span(62, 1), new Span(71, 1)), result.Select(ts => ts.Span.Span).OrderBy(s => s.Start));
 
        // After switch open paren
        result = await ProduceTagsAsync(workspace, buffer, 83);
        Assert.True(result.IsEmpty());
 
        // At switch close paren
        result = await ProduceTagsAsync(workspace, buffer, 71);
        Assert.True(result.IsEmpty());
 
        // After switch close paren
        result = await ProduceTagsAsync(workspace, buffer, 72);
        AssertEx.Equal(Enumerable(new Span(62, 1), new Span(71, 1)), result.Select(ts => ts.Span.Span).OrderBy(s => s.Start));
 
        // At switch open curly
        result = await ProduceTagsAsync(workspace, buffer, 82);
        AssertEx.Equal(Enumerable(new Span(82, 1), new Span(138, 1)), result.Select(ts => ts.Span.Span).OrderBy(s => s.Start));
 
        // After switch open curly
        result = await ProduceTagsAsync(workspace, buffer, 83);
        Assert.True(result.IsEmpty());
 
        // At switch close curly
        result = await ProduceTagsAsync(workspace, buffer, 138);
        Assert.True(result.IsEmpty());
 
        // After switch close curly
        result = await ProduceTagsAsync(workspace, buffer, 139);
        AssertEx.Equal(Enumerable(new Span(82, 1), new Span(138, 1)), result.Select(ts => ts.Span.Span).OrderBy(s => s.Start));
    }
}