File: Language\CodeGeneration\DesignTimeNodeWriter.cs
Web Access
Project: src\src\Razor\src\Compiler\Microsoft.CodeAnalysis.Razor.Compiler\src\Microsoft.CodeAnalysis.Razor.Compiler.csproj (Microsoft.CodeAnalysis.Razor.Compiler)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Diagnostics;
using Microsoft.AspNetCore.Razor.Language.Extensions;
using Microsoft.AspNetCore.Razor.Language.Intermediate;
 
namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration;
 
public class DesignTimeNodeWriter : IntermediateNodeWriter
{
    public static readonly DesignTimeNodeWriter Instance = new DesignTimeNodeWriter();
 
    private DesignTimeNodeWriter()
    {
    }
 
    public override void WriteUsingDirective(CodeRenderingContext context, UsingDirectiveIntermediateNode node)
    {
        if (node.Source is { FilePath: not null } sourceSpan)
        {
            using (context.BuildLinePragma(sourceSpan, suppressLineDefaultAndHidden: !node.AppendLineDefaultAndHidden))
            {
                context.AddSourceMappingFor(node);
                context.CodeWriter.WriteUsing(node.Content);
            }
        }
        else
        {
            context.CodeWriter.WriteUsing(node.Content);
 
            if (node.AppendLineDefaultAndHidden)
            {
                context.CodeWriter.WriteLine("#line default");
                context.CodeWriter.WriteLine("#line hidden");
            }
        }
    }
 
    public override void WriteCSharpExpression(CodeRenderingContext context, CSharpExpressionIntermediateNode node)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }
 
        if (node == null)
        {
            throw new ArgumentNullException(nameof(node));
        }
 
        if (node.Children.Count == 0)
        {
            return;
        }
 
        if (node.Source != null)
        {
            using (context.BuildLinePragma(node.Source.Value))
            {
                var offset = DesignTimeDirectivePass.DesignTimeVariable.Length + " = ".Length;
                context.CodeWriter.WritePadding(offset, node.Source, context);
                context.CodeWriter.WriteStartAssignment(DesignTimeDirectivePass.DesignTimeVariable);
 
                foreach (var child in node.Children)
                {
                    if (child is CSharpIntermediateToken token)
                    {
                        context.AddSourceMappingFor(token);
                        context.CodeWriter.Write(token.Content);
                    }
                    else
                    {
                        // There may be something else inside the expression like a Template or another extension node.
                        context.RenderNode(child);
                    }
                }
 
                context.CodeWriter.WriteLine(";");
            }
        }
        else
        {
            context.CodeWriter.WriteStartAssignment(DesignTimeDirectivePass.DesignTimeVariable);
 
            foreach (var child in node.Children)
            {
                if (child is CSharpIntermediateToken token)
                {
                    context.CodeWriter.Write(token.Content);
                }
                else
                {
                    // There may be something else inside the expression like a Template or another extension node.
                    context.RenderNode(child);
                }
            }
 
            context.CodeWriter.WriteLine(";");
        }
    }
 
    public override void WriteCSharpCode(CodeRenderingContext context, CSharpCodeIntermediateNode node)
    {
        var writer = context.CodeWriter;
 
        if (node.Source is SourceSpan nodeSource)
        {
            using (context.BuildLinePragma(nodeSource))
            {
                writer.WritePadding(0, nodeSource, context);
                RenderCSharpCode(context, node);
            }
        }
        else
        {
            RenderCSharpCode(context, node);
            writer.WriteLine();
        }
    }
 
    public override void WriteHtmlAttribute(CodeRenderingContext context, HtmlAttributeIntermediateNode node)
    {
        context.RenderChildren(node);
    }
 
    public override void WriteHtmlAttributeValue(CodeRenderingContext context, HtmlAttributeValueIntermediateNode node)
    {
        context.RenderChildren(node);
    }
 
    public override void WriteCSharpExpressionAttributeValue(CodeRenderingContext context, CSharpExpressionAttributeValueIntermediateNode node)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }
 
        if (node == null)
        {
            throw new ArgumentNullException(nameof(node));
        }
 
        if (node.Children.Count == 0)
        {
            return;
        }
 
        var firstChild = node.Children[0];
        if (firstChild.Source != null)
        {
            using (context.BuildLinePragma(firstChild.Source.Value))
            {
                var offset = DesignTimeDirectivePass.DesignTimeVariable.Length + " = ".Length;
                context.CodeWriter.WritePadding(offset, firstChild.Source, context);
                context.CodeWriter.WriteStartAssignment(DesignTimeDirectivePass.DesignTimeVariable);
 
                foreach (var child in node.Children)
                {
                    if (child is CSharpIntermediateToken token)
                    {
                        context.AddSourceMappingFor(token);
                        context.CodeWriter.Write(token.Content);
                    }
                    else
                    {
                        // There may be something else inside the expression like a Template or another extension node.
                        context.RenderNode(child);
                    }
                }
 
                context.CodeWriter.WriteLine(";");
            }
        }
        else
        {
            context.CodeWriter.WriteStartAssignment(DesignTimeDirectivePass.DesignTimeVariable);
            for (var i = 0; i < node.Children.Count; i++)
            {
                if (node.Children[i] is CSharpIntermediateToken token)
                {
                    if (token.Source != null)
                    {
                        context.AddSourceMappingFor(token);
                    }
 
                    context.CodeWriter.Write(token.Content);
                }
                else
                {
                    // There may be something else inside the expression like a Template or another extension node.
                    context.RenderNode(node.Children[i]);
                }
            }
            context.CodeWriter.WriteLine(";");
        }
    }
 
    public override void WriteCSharpCodeAttributeValue(CodeRenderingContext context, CSharpCodeAttributeValueIntermediateNode node)
    {
        var writer = context.CodeWriter;
 
        foreach (var child in node.Children)
        {
            if (child is CSharpIntermediateToken token)
            {
                var isWhiteSpace = token.Content.IsNullOrWhiteSpace();
 
                if (token.Source is not SourceSpan tokenSource)
                {
                    // Just write non-whitespace tokens when there isn't a source mapping.
                    if (!isWhiteSpace)
                    {
                        Debug.Fail("Why do we have non-whitespace tokens without source mappings?");
                        writer.WriteLine(token.Content);
                    }
 
                    continue;
                }
 
                if (!isWhiteSpace)
                {
                    // Only use a line pragma for non-whitespace content.
                    using (context.BuildLinePragma(tokenSource))
                    {
                        writer.WritePadding(0, tokenSource, context);
 
                        context.AddSourceMappingFor(tokenSource);
                        writer.Write(token.Content);
                    }
                }
                else
                {
                    writer.WritePadding(0, tokenSource, context);
 
                    context.AddSourceMappingFor(tokenSource);
                    writer.WriteLine(token.Content);
                }
            }
            else
            {
                // There may be something else inside the statement like an extension node.
                context.RenderNode(child);
            }
        }
    }
 
    public override void WriteHtmlContent(CodeRenderingContext context, HtmlContentIntermediateNode node)
    {
        // Do nothing
    }
 
    public override void BeginWriterScope(CodeRenderingContext context, string writer)
    {
        // Do nothing
    }
 
    public override void EndWriterScope(CodeRenderingContext context)
    {
        // Do nothing
    }
}