Skip to content

Commit 28828d9

Browse files
authored
Format type parameter values as types (#12448)
Fixes #12445 With our generic "code-gen as though everything is an expression", Roslyn was formatting `Guid?` as `Guid ?`
2 parents 73833fd + e8adbb1 commit 28828d9

File tree

2 files changed

+47
-6
lines changed

2 files changed

+47
-6
lines changed

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

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Text;
99
using Microsoft.AspNetCore.Razor;
1010
using Microsoft.AspNetCore.Razor.Language;
11+
using Microsoft.AspNetCore.Razor.Language.Components;
1112
using Microsoft.AspNetCore.Razor.Language.Syntax;
1213
using Microsoft.AspNetCore.Razor.PooledObjects;
1314
using Microsoft.CodeAnalysis.ExternalAccess.Razor.Features;
@@ -214,12 +215,7 @@ public void Generate()
214215
var node = root.FindInnermostNode(originalSpan.AbsoluteIndex);
215216
if (node is CSharpExpressionLiteralSyntax)
216217
{
217-
// Rather than bother to store more data about the formatted file, since we don't actually know where
218-
// these will end up in that file once it's all said and done, we are just going to use a simple comment
219-
// format that we can easily parse.
220-
additionalLinesBuilder.AppendLine(GetAdditionalLineComment(originalSpan));
221-
additionalLinesBuilder.AppendLine(_sourceText.GetSubTextString(originalSpan.ToTextSpan()));
222-
additionalLinesBuilder.AppendLine(";");
218+
AddAdditionalLineFormattingContent(additionalLinesBuilder, node, originalSpan);
223219
}
224220

225221
iMapping++;
@@ -254,6 +250,34 @@ public void Generate()
254250
_builder.AppendLine(additionalLinesBuilder.ToString());
255251
}
256252

253+
private void AddAdditionalLineFormattingContent(StringBuilder additionalLinesBuilder, RazorSyntaxNode node, SourceSpan originalSpan)
254+
{
255+
// Rather than bother to store more data about the formatted file, since we don't actually know where
256+
// these will end up in that file once it's all said and done, we are just going to use a simple comment
257+
// format that we can easily parse.
258+
259+
// Special case, for attributes that represent generic type parameters, we want to output something such
260+
// that Roslyn knows to format it as a type. For example, the meaning and spacing around "?"s should be
261+
// what the user expects.
262+
if (node is { Parent.Parent: MarkupTagHelperAttributeSyntax attribute } &&
263+
attribute is { Parent.Parent: MarkupTagHelperElementSyntax element } &&
264+
element.TagHelperInfo.BindingResult.Descriptors is [{ } descriptor] &&
265+
descriptor.IsGenericTypedComponent() &&
266+
descriptor.BoundAttributes.FirstOrDefault(d => d.Name == attribute.TagHelperAttributeInfo.Name) is { } boundAttribute &&
267+
boundAttribute.IsTypeParameterProperty())
268+
{
269+
additionalLinesBuilder.AppendLine("F<");
270+
additionalLinesBuilder.AppendLine(GetAdditionalLineComment(originalSpan));
271+
additionalLinesBuilder.AppendLine(_sourceText.GetSubTextString(originalSpan.ToTextSpan()));
272+
additionalLinesBuilder.AppendLine("> x;");
273+
return;
274+
}
275+
276+
additionalLinesBuilder.AppendLine(GetAdditionalLineComment(originalSpan));
277+
additionalLinesBuilder.AppendLine(_sourceText.GetSubTextString(originalSpan.ToTextSpan()));
278+
additionalLinesBuilder.AppendLine(";");
279+
}
280+
257281
public override LineInfo Visit(RazorSyntaxNode? node)
258282
{
259283
// Sometimes we are in a block where we want to do no formatting at all

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7303,4 +7303,21 @@ public Task NestedExplicitExpression4()
73037303
</span>
73047304
}
73057305
""");
7306+
7307+
[FormattingTestFact]
7308+
[WorkItem("https://github.com/dotnet/razor/issues/12445")]
7309+
public Task TypeParameterAttribute()
7310+
=> RunFormattingTestAsync(
7311+
input: """
7312+
<div>
7313+
<InputSelect TValue="Guid?">
7314+
</InputSelect>
7315+
</div>
7316+
""",
7317+
expected: """
7318+
<div>
7319+
<InputSelect TValue="Guid?">
7320+
</InputSelect>
7321+
</div>
7322+
""");
73067323
}

0 commit comments

Comments
 (0)