|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.StringIndentation;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.StringIndentation;
[UseExportProvider]
[Trait(Traits.Feature, Traits.Features.StringIndentation)]
public class StringIndentationTests
{
private static async Task TestAsync(string contents)
{
using var workspace = TestWorkspace.CreateWorkspace(
TestWorkspace.CreateWorkspaceElement(LanguageNames.CSharp,
files: [contents.Replace("|", " ")],
isMarkup: false));
var document = workspace.CurrentSolution.GetRequiredDocument(workspace.Documents.First().Id);
var root = await document.GetRequiredSyntaxRootAsync(default);
var service = document.GetRequiredLanguageService<IStringIndentationService>();
var regions = await service.GetStringIndentationRegionsAsync(document, root.FullSpan, CancellationToken.None).ConfigureAwait(false);
var actual = ApplyRegions(contents.Replace("|", " "), regions);
Assert.Equal(contents, actual);
}
private static string ApplyRegions(string val, ImmutableArray<StringIndentationRegion> regions)
{
var text = SourceText.From(val);
using var _ = ArrayBuilder<TextChange>.GetInstance(out var changes);
foreach (var region in regions)
{
var firstLine = text.Lines.GetLineFromPosition(region.IndentSpan.Start);
var lastLine = text.Lines.GetLineFromPosition(region.IndentSpan.End);
var offset = region.IndentSpan.End - lastLine.Start;
for (var i = firstLine.LineNumber + 1; i < lastLine.LineNumber; i++)
{
var lineStart = text.Lines[i].Start;
if (region.OrderedHoleSpans.Any(s => s.Contains(lineStart)))
continue;
changes.Add(new TextChange(new TextSpan(lineStart + offset - 1, 1), "|"));
}
}
var changedText = text.WithChanges(changes);
return changedText.ToString();
}
[Fact]
public async Task TestEmptyFile()
=> await TestAsync(string.Empty);
[Theory]
[InlineData("")]
[InlineData("u8")]
public async Task TestLiteralError1(string suffix)
{
await TestAsync($@"class C
{{
void M()
{{
// not enough lines in literal
var v = """"""
""""""{suffix};
}}
}}");
}
[Theory]
[InlineData("")]
[InlineData("u8")]
public async Task TestLiteralError2(string suffix)
{
await TestAsync($@"class C
{{
void M()
{{
// invalid literal
var v = """"""
text too early
""""""{suffix};
}}
}}");
}
[Theory]
[InlineData("")]
[InlineData("u8")]
public async Task TestZeroColumn1(string suffix)
{
await TestAsync($@"class C
{{
void M()
{{
var v = """"""
goo
""""""{suffix};
}}
}}");
}
[Theory]
[InlineData("")]
[InlineData("u8")]
public async Task TestZeroColumn2(string suffix)
{
await TestAsync($@"class C
{{
void M()
{{
var v = """"""
goo
""""""{suffix};
}}
}}");
}
[Theory]
[InlineData("")]
[InlineData("u8")]
public async Task TestOneColumn1(string suffix)
{
await TestAsync($@"class C
{{
void M()
{{
var v = """"""
|goo
""""""{suffix};
}}
}}");
}
[Theory]
[InlineData("")]
[InlineData("u8")]
public async Task TestOneColumn2(string suffix)
{
await TestAsync($@"class C
{{
void M()
{{
var v = """"""
| goo
""""""{suffix};
}}
}}");
}
[Theory]
[InlineData("")]
[InlineData("u8")]
public async Task TestCase1(string suffix)
{
await TestAsync($@"class C
{{
void M()
{{
var v = """"""
|goo
""""""{suffix};
}}
}}");
}
[Theory]
[InlineData("")]
[InlineData("u8")]
public async Task TestCase2(string suffix)
{
await TestAsync($@"class C
{{
void M()
{{
var v = """"""
|goo
|bar
""""""{suffix};
}}
}}");
}
[Theory]
[InlineData("")]
[InlineData("u8")]
public async Task TestCase3(string suffix)
{
await TestAsync($@"class C
{{
void M()
{{
var v = """"""
|goo
|bar
|baz
""""""{suffix};
}}
}}");
}
[Theory]
[InlineData("")]
[InlineData("u8")]
public async Task TestCase4(string suffix)
{
await TestAsync($@"class C
{{
void M()
{{
var v = """"""
|goo
|
|baz
""""""{suffix};
}}
}}");
}
[Theory]
[InlineData("")]
[InlineData("u8")]
public async Task TestCase5(string suffix)
{
await TestAsync($@"class C
{{
void M()
{{
var v = """"""
| goo
|
| baz
""""""{suffix};
}}
}}");
}
[Theory]
[InlineData("")]
[InlineData("u8")]
public async Task TestCase6(string suffix)
{
await TestAsync($@"class C
{{
void M()
{{
var v =
$""""""
|goo
""""""{suffix};
}}
}}");
}
[Theory]
[InlineData("")]
[InlineData("u8")]
public async Task TestCase7(string suffix)
{
await TestAsync($@"class C
{{
void M()
{{
var v =
$""""""
|goo
""""""{suffix};
}}
}}");
}
[Theory]
[InlineData("")]
[InlineData("u8")]
public async Task TestCase8(string suffix)
{
await TestAsync($@"class C
{{
void M()
{{
var v =
$""""""""
|goo
""""""""{suffix};
}}
}}");
}
[Theory]
[InlineData("")]
[InlineData("u8")]
public async Task TestCase9(string suffix)
{
await TestAsync($@"class C
{{
void M()
{{
var v =
""""""""
|goo
""""""""{suffix};
}}
}}");
}
[Fact]
public async Task TestCase10()
{
await TestAsync("""""
class C
{
void M()
{
var v =
$$""""
|goo
"""";
}
}
""""");
}
[Fact]
public async Task TestCase11()
{
await TestAsync("""""
class C
{
void M()
{
var v =
$$""""
|goo
"""";
}
}
""""");
}
[Fact]
public async Task TestCase12()
{
await TestAsync("""""
class C
{
void M()
{
var v =
$$""""
|goo
"""";
}
}
""""");
}
[Fact]
public async Task TestWithHoles1()
{
await TestAsync(""""
class C
{
void M()
{
var v = $"""
| goo
| { 1 + 1 }
| baz
""";
}
}
"""");
}
[Fact]
public async Task TestWithHoles2()
{
await TestAsync(""""
class C
{
void M()
{
var v = $"""
| goo{
| 1 + 1
| }baz
""";
}
}
"""");
}
[Fact]
public async Task TestWithHoles3()
{
await TestAsync(""""
class C
{
void M()
{
var v = $"""
| goo{
|1 + 1
| }baz
""";
}
}
"""");
}
[Fact]
public async Task TestWithHoles4()
{
await TestAsync(""""
class C
{
void M()
{
var v = $"""
| goo{
1 + 1
}baz
""";
}
}
"""");
}
[Fact]
public async Task TestWithHoles5()
{
await TestAsync(""""
class C
{
void M()
{
var v = $"""
| goo{
|1 + 1
| }baz
| quux
""";
}
}
"""");
}
[Fact]
public async Task TestWithHoles6()
{
await TestAsync(""""
class C
{
void M()
{
var v = $"""
| goo{
1 + 1
}baz
| quux
""";
}
}
"""");
}
[Fact]
public async Task TestWithHoles7()
{
await TestAsync(""""
class C
{
void M()
{
var v = $"""
|goo{
1 + 1
}baz
|quux
""";
}
}
"""");
}
[Fact]
public async Task TestWithHoles8()
{
await TestAsync(""""
class C
{
void M()
{
var v = $"""
| { 1 + 1 }
| baz
""";
}
}
"""");
}
[Fact]
public async Task TestWithHoles9()
{
await TestAsync(""""
class C
{
void M()
{
var v = $"""
| {
| 1 + 1
| }
| baz
""";
}
}
"""");
}
[Fact]
public async Task TestWithHoles10()
{
await TestAsync(""""
class C
{
void M()
{
var v = $"""
| {
1 + 1
}
| baz
""";
}
}
"""");
}
[Fact]
public async Task TestWithNestedHoles1()
{
await TestAsync(""""
class C
{
void M()
{
var x =
$"""
|goo
|{
| $"""
| |bar
| """
|}
|baz
""";
}
}
"""");
}
[Fact]
public async Task TestWithNestedHoles2()
{
await TestAsync(""""
class C
{
void M()
{
var x =
$"""
|goo
|{
| $"""
| |bar
| |{
| | 1 + 1
| |}
| """
|}
|baz
""";
}
}
"""");
}
[Fact]
public async Task TestWithNestedHoles3()
{
await TestAsync(""""
class C
{
void M()
{
var x =
$"""
|goo
|{
| $"""
| |bar
| |{
| 1 + 1
| }
| """
|}
|baz
""";
}
}
"""");
}
[Fact]
public async Task TestWithNestedHoles4()
{
await TestAsync(""""
class C
{
void M()
{
var x =
$"""
|goo
|{
$"""
|bar
|{
1 + 1
}
"""
}
|baz
""";
}
}
"""");
}
[Fact]
public async Task TestWithNestedHoles5()
{
await TestAsync(""""
class C
{
void M()
{
var x =
$"""
|goo
|{
$"""
|bar
"""
}
|baz
""";
}
}
"""");
}
[Fact]
public async Task TestWithNestedHoles6()
{
await TestAsync(""""
class C
{
void M()
{
var x =
$"""
|goo
|{
$"""
|bar
|{
| 1 + 1
|}
"""
}
|baz
""";
}
}
"""");
}
[Fact]
public async Task TestWithNestedHoles7()
{
await TestAsync(""""
class C
{
void M()
{
var x =
$"""
|goo
|{
$"""
|bar
|{
1 + 1
}
"""
}
|baz
""";
}
}
"""");
}
[Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1542623")]
public async Task TestWithManyConcatenatedStrings()
{
var input = new StringBuilder(
"""
class C
{
void M()
{
_ =
""");
for (var i = 0; i < 2000; i++)
{
input.AppendLine(
"""
@"" + "" + @"" + "" + @"" + "" + @"" + "" + @"" + "" + @"" + "" + @"" +
""");
}
input.AppendLine(
"""
@"" + "" + @"" + "" + @"" + "" + @"" + "" + @"" + "" + @"" + "" + @"";
}
}
""");
await TestAsync(input.ToString());
}
}
|