Skip to content

Commit bbeb286

Browse files
authored
Fix one formatting bug, and prevent another from crashing the formatter (#12786)
From https://developercommunity.visualstudio.com/t/Razor-Formatting-Feature-internal-error/11041869#T-ND11043454 First commit is a fix for formatting when there are two markup elements in the same line, inside a C# block. Second commit is ensuring we don't crash when the document generation produces bad data. The bad data comes from us not handling block bodied lambdas in attributes properly, for which I've logged #12785 to fix at some point. Thought it was worth at least unblocking users for now though.
2 parents 93f74c0 + 694abf3 commit bbeb286

5 files changed

Lines changed: 85 additions & 3 deletions

File tree

src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Formatting/FormattingUtilities.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,11 @@ public static void GetOriginalDocumentChangesFromLineInfo(FormattingContext cont
407407
? originalLine.End - originalStart
408408
: lineInfo.FormattedLength;
409409
var formattedStart = formattedLine.Start + formattedIndentation + lineInfo.FormattedOffset;
410-
formattingChanges.Add(new TextChange(new TextSpan(originalStart, length), formattedText.ToString(TextSpan.FromBounds(formattedStart, formattedLine.End - lineInfo.FormattedOffsetFromEndOfLine))));
410+
var formattedEnd = formattedLine.End - lineInfo.FormattedOffsetFromEndOfLine;
411+
if (formattedEnd > formattedStart)
412+
{
413+
formattingChanges.Add(new TextChange(new TextSpan(originalStart, length), formattedText.ToString(TextSpan.FromBounds(formattedStart, formattedEnd))));
414+
}
411415

412416
if (lineInfo.CheckForNewLines)
413417
{

src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Formatting/Passes/RazorFormattingPass.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,13 @@ private static bool FormatBlock(FormattingContext context, RazorSourceDocument s
380380
return didFormat;
381381
}
382382

383+
// When there are multiple sibling markup blocks in a C# code block (e.g., "<text>:</text> <InputFile />"),
384+
// the open/close brace nodes passed to this method may actually be sibling markup blocks rather than
385+
// actual braces. We should not try to format between sibling markup blocks as they should remain on the
386+
// same line.
387+
var openBraceIsMarkupBlock = openBraceNode is MarkupBlockSyntax && openBraceNode != codeNode;
388+
var closeBraceIsMarkupBlock = closeBraceNode is MarkupBlockSyntax && closeBraceNode != codeNode;
389+
383390
var additionalIndentation = "";
384391

385392
// It's important with the new formatting engine that we maintain the indentation that the Html formatter would have applied,
@@ -401,7 +408,8 @@ private static bool FormatBlock(FormattingContext context, RazorSourceDocument s
401408
additionalIndentation = openBraceLine.GetLeadingWhitespace();
402409
}
403410

404-
if (openBraceNode.TryGetLinePositionSpanWithoutWhitespace(source, out var openBraceRange) &&
411+
if (!openBraceIsMarkupBlock &&
412+
openBraceNode.TryGetLinePositionSpanWithoutWhitespace(source, out var openBraceRange) &&
405413
openBraceRange.End.Line == codeRange.Start.Line &&
406414
!RangeHasBeenModified(ref changes, source.Text, codeRange))
407415
{
@@ -411,7 +419,8 @@ private static bool FormatBlock(FormattingContext context, RazorSourceDocument s
411419
didFormat = true;
412420
}
413421

414-
if (closeBraceNode.Span.Length > 0 &&
422+
if (!closeBraceIsMarkupBlock &&
423+
closeBraceNode.Span.Length > 0 &&
415424
closeBraceNode.TryGetLinePositionSpanWithoutWhitespace(source, out var closeBraceRange) &&
416425
!RangeHasBeenModified(ref changes, source.Text, codeRange))
417426
{

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

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,56 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting;
1818

1919
public class DocumentFormattingTest(ITestOutputHelper testOutput) : DocumentFormattingTestBase(testOutput)
2020
{
21+
[Fact]
22+
[WorkItem("https://developercommunity.visualstudio.com/t/Razor-Formatting-Feature-internal-error/11041869")]
23+
public async Task TextAndTagOnSameLine()
24+
{
25+
await RunFormattingTestAsync(
26+
input: """
27+
<div>
28+
@if (b)
29+
{
30+
<text>:</text> <InputFile OnChange="StateHasChanged" />
31+
}
32+
</div>
33+
34+
@code
35+
{
36+
bool b;
37+
}
38+
39+
""",
40+
htmlFormatted: """
41+
<div>
42+
@if (b)
43+
{
44+
<text>:</text> <InputFile OnChange="StateHasChanged" />
45+
}
46+
</div>
47+
48+
@code
49+
{
50+
bool b;
51+
}
52+
53+
""",
54+
expected: """
55+
<div>
56+
@if (b)
57+
{
58+
<text>:</text> <InputFile OnChange="StateHasChanged" />
59+
}
60+
</div>
61+
62+
@code
63+
{
64+
bool b;
65+
}
66+
67+
""",
68+
insertSpaces: false);
69+
}
70+
2171
[Fact]
2272
[WorkItem("https://github.com/microsoft/vscode-dotnettools/issues/2766")]
2373
public async Task DifferentAttributeWrappingPoint1()

src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/Formatting/FormattingLogTest.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,22 @@ public async Task RanOutOfOriginalLines()
8888
await GetFormattingEditsAsync(contents, htmlChangesFile);
8989
}
9090

91+
[Fact]
92+
[WorkItem("https://developercommunity.visualstudio.com/t/Razor-Formatting-Feature-internal-error/11041869#T-ND11043454")]
93+
public async Task MultiLineLambda()
94+
{
95+
var contents = GetResource("InitialDocument.txt");
96+
97+
var document = CreateProjectAndRazorDocument(contents);
98+
99+
var options = new RazorFormattingOptions();
100+
101+
var formattingService = (RazorFormattingService)OOPExportProvider.GetExportedValue<IRazorFormattingService>();
102+
formattingService.GetTestAccessor().SetFormattingLoggerFactory(new TestFormattingLoggerFactory(TestOutputHelper));
103+
104+
await GetFormattingEditsAsync(document, [], span: default, options.CodeBlockBraceOnNextLine, options.AttributeIndentStyle, options.InsertSpaces, options.TabSize, RazorCSharpSyntaxFormattingOptions.Default);
105+
}
106+
91107
private async Task<TextEdit[]?> GetFormattingEditsAsync(string contents, string htmlChangesFile)
92108
{
93109
var document = CreateProjectAndRazorDocument(contents);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<button @onclick="()=>{
2+
StateHasChanged();}">
3+
</button>

0 commit comments

Comments
 (0)