Correctly handle whitespace that precedes a Razor directive (dotnet/aspnetcore-tooling#117)
\n\nCommit migrated from 15556c8ff3
This commit is contained in:
parent
9541938327
commit
d5e9a153c7
|
|
@ -77,11 +77,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CSharpCodeIntermediateNode codeIntermediateNode:
|
case CSharpCodeIntermediateNode codeIntermediateNode:
|
||||||
// A C# code node could be empty. We can't remove them, but we can skip them.
|
|
||||||
shouldRemoveNode = false;
|
shouldRemoveNode = false;
|
||||||
shouldContinueIteration =
|
shouldContinueIteration = ComponentDocumentClassifierPass.IsBuildRenderTreeBaseCall(codeIntermediateNode);
|
||||||
IsEmpty(codeIntermediateNode) ||
|
|
||||||
ComponentDocumentClassifierPass.IsBuildRenderTreeBaseCall(codeIntermediateNode);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
@ -113,18 +110,5 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||||
Forwards,
|
Forwards,
|
||||||
Backwards
|
Backwards
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsEmpty(CSharpCodeIntermediateNode node)
|
|
||||||
{
|
|
||||||
for (var i = 0; i < node.Children.Count; i++)
|
|
||||||
{
|
|
||||||
if (!(node.Children[i] is IntermediateToken token && string.IsNullOrWhiteSpace(token.Content)))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -124,9 +124,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
{
|
{
|
||||||
NextToken();
|
NextToken();
|
||||||
|
|
||||||
// Unless changed, the block is a statement block
|
var precedingWhitespace = ReadWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
|
||||||
AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
|
|
||||||
builder.Add(OutputTokensAsStatementLiteral());
|
|
||||||
|
|
||||||
// We are usually called when the other parser sees a transition '@'. Look for it.
|
// We are usually called when the other parser sees a transition '@'. Look for it.
|
||||||
SyntaxToken transitionToken = null;
|
SyntaxToken transitionToken = null;
|
||||||
|
|
@ -157,20 +155,33 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
if (At(SyntaxKind.LeftBrace))
|
if (At(SyntaxKind.LeftBrace))
|
||||||
{
|
{
|
||||||
|
// This is a statement. We want to preserve preceding whitespace in the output.
|
||||||
|
Accept(precedingWhitespace);
|
||||||
|
builder.Add(OutputTokensAsStatementLiteral());
|
||||||
|
|
||||||
var statementBody = ParseStatementBody();
|
var statementBody = ParseStatementBody();
|
||||||
var statement = SyntaxFactory.CSharpStatement(transition, statementBody);
|
var statement = SyntaxFactory.CSharpStatement(transition, statementBody);
|
||||||
builder.Add(statement);
|
builder.Add(statement);
|
||||||
}
|
}
|
||||||
else if (At(SyntaxKind.LeftParenthesis))
|
else if (At(SyntaxKind.LeftParenthesis))
|
||||||
{
|
{
|
||||||
|
// This is an explicit expression. We want to preserve preceding whitespace in the output.
|
||||||
|
Accept(precedingWhitespace);
|
||||||
|
builder.Add(OutputTokensAsStatementLiteral());
|
||||||
|
|
||||||
var expressionBody = ParseExplicitExpressionBody();
|
var expressionBody = ParseExplicitExpressionBody();
|
||||||
var expression = SyntaxFactory.CSharpExplicitExpression(transition, expressionBody);
|
var expression = SyntaxFactory.CSharpExplicitExpression(transition, expressionBody);
|
||||||
builder.Add(expression);
|
builder.Add(expression);
|
||||||
}
|
}
|
||||||
else if (At(SyntaxKind.Identifier))
|
else if (At(SyntaxKind.Identifier))
|
||||||
{
|
{
|
||||||
if (!TryParseDirective(builder, transition, CurrentToken.Content))
|
if (!TryParseDirective(builder, precedingWhitespace, transition, CurrentToken.Content))
|
||||||
{
|
{
|
||||||
|
// Not a directive.
|
||||||
|
// This is an implicit expression. We want to preserve preceding whitespace in the output.
|
||||||
|
Accept(precedingWhitespace);
|
||||||
|
builder.Add(OutputTokensAsStatementLiteral());
|
||||||
|
|
||||||
if (string.Equals(
|
if (string.Equals(
|
||||||
CurrentToken.Content,
|
CurrentToken.Content,
|
||||||
SyntaxConstants.CSharp.HelperKeyword,
|
SyntaxConstants.CSharp.HelperKeyword,
|
||||||
|
|
@ -189,9 +200,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
}
|
}
|
||||||
else if (At(SyntaxKind.Keyword))
|
else if (At(SyntaxKind.Keyword))
|
||||||
{
|
{
|
||||||
if (!TryParseDirective(builder, transition, CurrentToken.Content) &&
|
if (!TryParseDirective(builder, precedingWhitespace, transition, CurrentToken.Content) &&
|
||||||
!TryParseKeyword(builder, transition))
|
!TryParseKeyword(builder, precedingWhitespace, transition))
|
||||||
{
|
{
|
||||||
|
// Not a directive or keyword.
|
||||||
|
// This is an implicit expression. We want to preserve preceding whitespace in the output.
|
||||||
|
Accept(precedingWhitespace);
|
||||||
|
builder.Add(OutputTokensAsStatementLiteral());
|
||||||
|
|
||||||
// Not a directive or a special keyword. Just parse as an implicit expression.
|
// Not a directive or a special keyword. Just parse as an implicit expression.
|
||||||
var implicitExpressionBody = ParseImplicitExpressionBody();
|
var implicitExpressionBody = ParseImplicitExpressionBody();
|
||||||
var implicitExpression = SyntaxFactory.CSharpImplicitExpression(transition, implicitExpressionBody);
|
var implicitExpression = SyntaxFactory.CSharpImplicitExpression(transition, implicitExpressionBody);
|
||||||
|
|
@ -202,7 +218,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Invalid character
|
// Invalid character after transition.
|
||||||
|
// Preserve the preceding whitespace in the output
|
||||||
|
Accept(precedingWhitespace);
|
||||||
|
builder.Add(OutputTokensAsStatementLiteral());
|
||||||
|
|
||||||
SpanContext.ChunkGenerator = new ExpressionChunkGenerator();
|
SpanContext.ChunkGenerator = new ExpressionChunkGenerator();
|
||||||
SpanContext.EditHandler = new ImplicitExpressionEditHandler(
|
SpanContext.EditHandler = new ImplicitExpressionEditHandler(
|
||||||
Language.TokenizeString,
|
Language.TokenizeString,
|
||||||
|
|
@ -616,7 +636,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
ParseCodeBlock(builder, block);
|
ParseCodeBlock(builder, block);
|
||||||
break;
|
break;
|
||||||
case SyntaxKind.Keyword:
|
case SyntaxKind.Keyword:
|
||||||
if (!TryParseKeyword(builder, transition: null))
|
if (!TryParseKeyword(builder, whitespace: null, transition: null))
|
||||||
{
|
{
|
||||||
ParseStandardStatement(builder);
|
ParseStandardStatement(builder);
|
||||||
}
|
}
|
||||||
|
|
@ -797,10 +817,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected bool TryParseDirective(in SyntaxListBuilder<RazorSyntaxNode> builder, CSharpTransitionSyntax transition, string directive)
|
protected bool TryParseDirective(in SyntaxListBuilder<RazorSyntaxNode> builder, IEnumerable<SyntaxToken> whitespace, CSharpTransitionSyntax transition, string directive)
|
||||||
{
|
{
|
||||||
if (_directiveParserMap.TryGetValue(directive, out var handler))
|
if (_directiveParserMap.TryGetValue(directive, out var handler))
|
||||||
{
|
{
|
||||||
|
// This is a directive. We don't want to generate the preceding whitespace in the output.
|
||||||
|
Accept(whitespace);
|
||||||
|
builder.Add(OutputTokensAsEphemeralLiteral());
|
||||||
|
|
||||||
SpanContext.ChunkGenerator = SpanChunkGenerator.Null;
|
SpanContext.ChunkGenerator = SpanChunkGenerator.Null;
|
||||||
handler(builder, transition);
|
handler(builder, transition);
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -1544,12 +1568,19 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryParseKeyword(in SyntaxListBuilder<RazorSyntaxNode> builder, CSharpTransitionSyntax transition)
|
private bool TryParseKeyword(in SyntaxListBuilder<RazorSyntaxNode> builder, IEnumerable<SyntaxToken> whitespace, CSharpTransitionSyntax transition)
|
||||||
{
|
{
|
||||||
var result = CSharpTokenizer.GetTokenKeyword(CurrentToken);
|
var result = CSharpTokenizer.GetTokenKeyword(CurrentToken);
|
||||||
Debug.Assert(CurrentToken.Kind == SyntaxKind.Keyword && result.HasValue);
|
Debug.Assert(CurrentToken.Kind == SyntaxKind.Keyword && result.HasValue);
|
||||||
if (_keywordParserMap.TryGetValue(result.Value, out var handler))
|
if (_keywordParserMap.TryGetValue(result.Value, out var handler))
|
||||||
{
|
{
|
||||||
|
if (whitespace != null)
|
||||||
|
{
|
||||||
|
// This is a keyword. We want to preserve preceding whitespace in the output.
|
||||||
|
Accept(whitespace);
|
||||||
|
builder.Add(OutputTokensAsStatementLiteral());
|
||||||
|
}
|
||||||
|
|
||||||
handler(builder, transition);
|
handler(builder, transition);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue