File: Language\Components\ScopeStack.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.Collections.Generic;
using System.Diagnostics;
using Microsoft.AspNetCore.Razor.Language.CodeGeneration;
 
namespace Microsoft.AspNetCore.Razor.Language.Components;
 
/// <summary>
/// Keeps track of the nesting of elements/containers while writing out the C# source code
/// for a component. This allows us to detect mismatched start/end tags, as well as inject
/// additional C# source to capture component descendants in a lambda.
/// </summary>
internal sealed partial class ScopeStack
{
    private Entry _current;
    private Stack<Entry>? _stack;
 
    public BuilderVariableName BuilderVariableName => _current.BuilderVariableName;
    public RenderModeVariableName RenderModeVariableName => _current.RenderModeVariableName;
    public FormNameVariableName FormNameVariableName => _current.FormNameVariableName;
 
    public int Depth => _stack?.Count ?? 0;
 
    public ScopeStack()
    {
        _current = Entry.CreateFirst();
    }
 
    public readonly ref struct Scope(ScopeStack instance)
    {
        public void Dispose()
        {
            instance.CloseScope();
        }
    }
 
    public Scope OpenComponentScope(CodeRenderingContext context, string? parameterName)
    {
        // Writes code that looks like:
        //
        // ((__builder) => { ... })
        // OR
        // ((context) => (__builder) => { ... })
 
        if (parameterName != null)
        {
            context.CodeWriter.WriteLambdaHeader(parameterName);
        }
 
        return OpenScope(context);
    }
 
    public Scope OpenTemplateScope(CodeRenderingContext context)
        => OpenScope(context);
 
    private Scope OpenScope(CodeRenderingContext context)
    {
        _stack ??= new();
        _stack.Push(_current);
 
        _current = _current.Next(context);
 
        return new(this);
    }
 
    private void CloseScope()
    {
        Debug.Assert(_stack is not null && _stack.Count > 0);
 
        _current.Dispose();
        _current = _stack.Pop();
    }
 
    public void IncrementRenderMode() => _current.IncrementRenderMode();
 
    public void IncrementFormName() => _current.IncrementFormName();
}