Skip to content

Commit 6b19542

Browse files
committed
Explicitly deny certain Roslyn formatting options
1 parent 6c585b6 commit 6b19542

File tree

2 files changed

+138
-1
lines changed

2 files changed

+138
-1
lines changed

src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Formatting/Passes/New/CSharpFormattingPass.cs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
using System.Diagnostics;
77
using System.Threading;
88
using System.Threading.Tasks;
9+
using Microsoft.AspNetCore.Razor;
910
using Microsoft.AspNetCore.Razor.Language;
1011
using Microsoft.AspNetCore.Razor.PooledObjects;
1112
using Microsoft.CodeAnalysis.CSharp;
1213
using Microsoft.CodeAnalysis.ExternalAccess.Razor;
14+
using Microsoft.CodeAnalysis.ExternalAccess.Razor.Features;
1315
using Microsoft.CodeAnalysis.Razor.Logging;
1416
using Microsoft.CodeAnalysis.Razor.Workspaces;
1517
using Microsoft.CodeAnalysis.Text;
@@ -220,7 +222,30 @@ private async Task<SourceText> FormatCSharpAsync(SourceText generatedCSharpText,
220222

221223
var tree = CSharpSyntaxTree.ParseText(generatedCSharpText, cancellationToken: cancellationToken);
222224
var csharpRoot = await tree.GetRootAsync(cancellationToken).ConfigureAwait(false);
223-
var csharpChanges = RazorCSharpFormattingInteractionService.GetFormattedTextChanges(helper.HostWorkspaceServices, csharpRoot, csharpRoot.FullSpan, options.ToIndentationOptions(), options.CSharpSyntaxFormattingOptions, cancellationToken);
225+
var csharpSyntaxFormattingOptions = options.CSharpSyntaxFormattingOptions.AssumeNotNull();
226+
227+
// Roslyn can be configured to insert a space after a method call, or a dot, but that can break Razor. eg:
228+
//
229+
// <div>@PrintHello()</div>
230+
// @DateTime.Now.ToString()
231+
//
232+
// Would become:
233+
//
234+
// <div>@PrintHello ()</div>
235+
// @DateTime. Now. ToString()
236+
//
237+
// In Razor, that's not a method call, its a method group (ie C# compile error) followed by Html, and
238+
// the dot after DateTime is also just Html, as is the rest of the line.
239+
// We're not smart enough (yet?) to ignore this change when its inline in Razor, but allow it when
240+
// in a code block, so we just force these options to off.
241+
csharpSyntaxFormattingOptions = csharpSyntaxFormattingOptions with
242+
{
243+
Spacing = csharpSyntaxFormattingOptions.Spacing
244+
& ~RazorSpacePlacement.AfterMethodCallName
245+
& ~RazorSpacePlacement.AfterDot
246+
};
247+
248+
var csharpChanges = RazorCSharpFormattingInteractionService.GetFormattedTextChanges(helper.HostWorkspaceServices, csharpRoot, csharpRoot.FullSpan, options.ToIndentationOptions(), csharpSyntaxFormattingOptions, cancellationToken);
224249

225250
return generatedCSharpText.WithChanges(csharpChanges);
226251
}

src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting_NetFx/DocumentFormattingTest.cs

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,118 @@ await RunFormattingTestAsync(
2929
expected: "");
3030
}
3131

32+
[FormattingTestFact(SkipOldFormattingEngine = true)]
33+
public async Task RoslynFormatSpaceAfterDot()
34+
{
35+
await RunFormattingTestAsync(
36+
input: """
37+
<div>
38+
39+
@DateTime.Now.ToString()
40+
41+
</div>
42+
""",
43+
expected: """
44+
<div>
45+
46+
@DateTime.Now.ToString()
47+
48+
</div>
49+
""",
50+
csharpSyntaxFormattingOptions: RazorCSharpSyntaxFormattingOptions.Default with
51+
{
52+
Spacing = RazorSpacePlacement.AfterDot
53+
});
54+
}
55+
56+
[FormattingTestFact(SkipOldFormattingEngine = true)]
57+
public async Task RoslynFormatSpaceAfterMethodCall()
58+
{
59+
await RunFormattingTestAsync(
60+
input: """
61+
<h1>count is @counter</h1>
62+
63+
@GetCount()
64+
65+
@code {
66+
private int counter;
67+
68+
class Goo
69+
{
70+
public int GetCount()
71+
{
72+
return counter++;
73+
}
74+
}
75+
}
76+
""",
77+
expected: """
78+
<h1>count is @counter</h1>
79+
80+
@GetCount()
81+
82+
@code {
83+
private int counter;
84+
85+
class Goo
86+
{
87+
public int GetCount()
88+
{
89+
return counter++;
90+
}
91+
}
92+
}
93+
""",
94+
csharpSyntaxFormattingOptions: RazorCSharpSyntaxFormattingOptions.Default with
95+
{
96+
Spacing = RazorSpacePlacement.AfterMethodCallName
97+
});
98+
}
99+
100+
[FormattingTestFact(SkipOldFormattingEngine = true)]
101+
public async Task RoslynFormatSpaceAfterMethodCallAndDecl()
102+
{
103+
await RunFormattingTestAsync(
104+
input: """
105+
<h1>count is @counter</h1>
106+
107+
@GetCount()
108+
109+
@code {
110+
private int counter;
111+
112+
class Goo
113+
{
114+
public int GetCount()
115+
{
116+
return counter++;
117+
}
118+
}
119+
}
120+
""",
121+
expected: """
122+
<h1>count is @counter</h1>
123+
124+
@GetCount()
125+
126+
@code {
127+
private int counter;
128+
129+
class Goo
130+
{
131+
public int GetCount ()
132+
{
133+
return counter++;
134+
}
135+
}
136+
}
137+
""",
138+
csharpSyntaxFormattingOptions: RazorCSharpSyntaxFormattingOptions.Default with
139+
{
140+
Spacing = RazorSpacePlacement.AfterMethodCallName | RazorSpacePlacement.AfterMethodDeclarationName
141+
});
142+
}
143+
32144
[FormattingTestFact]
33145
public async Task RoslynFormatBracesAsKandR()
34146
{

0 commit comments

Comments
 (0)