Skip to content

Commit 1373cc5

Browse files
authored
Fix text area formatting in the new formatting engine (#11624)
2 parents 8cdb5f9 + dd19ee4 commit 1373cc5

File tree

2 files changed

+88
-6
lines changed

2 files changed

+88
-6
lines changed

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

+44-6
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,16 @@ private sealed class Generator(
141141
/// having to do lots of tree traversal.
142142
/// </remarks>
143143
private int? _elementEndLine;
144+
/// <summary>
145+
/// The line number of the last line of a block where formatting should be completely ignored
146+
/// </summary>
147+
/// <remarks>
148+
/// Some Html constructs, namely &lt;textarea&gt; and &lt;pre&gt;, should not be formatted at all, and we essentially
149+
/// need to treat them as multiline Razor comments. This field is used to track the line number of the last line of such
150+
/// an element, so we can ignore every line in it without having to do lots of tree traversal to check "are we parented
151+
/// by a pre tag" etc.
152+
/// </remarks>
153+
private int? _ignoreUntilLine;
144154

145155
public void Generate()
146156
{
@@ -206,12 +216,29 @@ public void Generate()
206216
{
207217
_elementEndLine = null;
208218
}
219+
220+
if (_ignoreUntilLine is { } endLine2 &&
221+
endLine2 == line.LineNumber)
222+
{
223+
_ignoreUntilLine = null;
224+
}
209225
}
210226

211227
_builder.AppendLine();
212228
_builder.AppendLine(additionalLinesBuilder.ToString());
213229
}
214230

231+
public override LineInfo Visit(RazorSyntaxNode node)
232+
{
233+
// Sometimes we are in a block where we want to do no formatting at all
234+
if (_ignoreUntilLine is not null)
235+
{
236+
return EmitCurrentLineWithNoFormatting();
237+
}
238+
239+
return base.Visit(node);
240+
}
241+
215242
protected override LineInfo DefaultVisit(RazorSyntaxNode node)
216243
{
217244
return EmitCurrentLineAsCSharp();
@@ -350,11 +377,17 @@ private LineInfo VisitCSharpLiteral(RazorSyntaxNode node, RazorSyntaxToken lastT
350377

351378
public override LineInfo VisitMarkupStartTag(MarkupStartTagSyntax node)
352379
{
353-
// If this is an element at the root level, we want to record where it ends. We can't rely on the Visit method
354-
// for it, because it might not be at the start of a line.
355-
if (_elementEndLine is null)
380+
var element = (MarkupElementSyntax)node.Parent;
381+
382+
if (node.Name.Content == "textarea")
356383
{
357-
var element = (MarkupElementSyntax)node.Parent;
384+
// The contents of textareas is significant, so we never want any formatting to happen inside them
385+
_ignoreUntilLine = GetLineNumber(element.EndTag?.CloseAngle ?? element.StartTag.CloseAngle);
386+
}
387+
else if (_elementEndLine is null)
388+
{
389+
// If this is an element at the root level, we want to record where it ends. We can't rely on the Visit method
390+
// for it, because it might not be at the start of a line.
358391
_elementEndLine = GetLineNumber(element.EndTag?.CloseAngle ?? element.StartTag.CloseAngle);
359392
}
360393

@@ -461,8 +494,7 @@ public override LineInfo VisitRazorCommentBlock(RazorCommentBlockSyntax node)
461494
}
462495

463496
// Do nothing for any lines inside the comment
464-
_builder.AppendLine();
465-
return CreateLineInfo(processIndentation: false);
497+
return EmitCurrentLineWithNoFormatting();
466498
}
467499

468500
public override LineInfo VisitCSharpTransition(CSharpTransitionSyntax node)
@@ -689,6 +721,12 @@ private LineInfo EmitCurrentLineAsComment()
689721
return CreateLineInfo();
690722
}
691723

724+
private LineInfo EmitCurrentLineWithNoFormatting()
725+
{
726+
_builder.AppendLine();
727+
return CreateLineInfo(processIndentation: false);
728+
}
729+
692730
private LineInfo CreateLineInfo(
693731
bool processIndentation = true,
694732
bool processFormatting = false,

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

+44
Original file line numberDiff line numberDiff line change
@@ -6038,4 +6038,48 @@ await RunFormattingTestAsync(
60386038
input: code,
60396039
expected: code);
60406040
}
6041+
6042+
[FormattingTestFact(SkipOldFormattingEngine = true)]
6043+
[WorkItem("https://github.com/dotnet/razor/issues/11622")]
6044+
public async Task TextArea()
6045+
{
6046+
var code = """
6047+
@page "/"
6048+
6049+
@if (true)
6050+
{
6051+
<textarea id="textarea1">
6052+
a
6053+
@if (true)
6054+
{
6055+
b
6056+
}
6057+
c
6058+
</textarea>
6059+
}
6060+
6061+
<textarea id="textarea2">
6062+
a
6063+
@if (true)
6064+
{
6065+
b
6066+
}
6067+
c
6068+
</textarea>
6069+
6070+
<div>
6071+
<textarea id="textarea3">
6072+
a
6073+
@if (true)
6074+
{
6075+
b
6076+
}
6077+
c
6078+
</textarea>
6079+
</div>
6080+
""";
6081+
await RunFormattingTestAsync(
6082+
input: code,
6083+
expected: code);
6084+
}
60416085
}

0 commit comments

Comments
 (0)