File: Language\Syntax\GreenNode.Enumerator.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.
 
namespace Microsoft.AspNetCore.Razor.Language.Syntax;
 
internal abstract partial class GreenNode
{
    /// <summary>
    ///  Provides a depth-first enumerator for traversing <see cref="GreenNode"/> syntax trees.
    ///  List nodes are treated as "transparent" and are not returned during enumeration,
    ///  only their children are processed.
    /// </summary>
    /// <remarks>
    ///  This enumerator uses a stack-based approach to traverse the syntax tree without recursion.
    ///  It pushes child nodes in reverse order to maintain correct left-to-right traversal order.
    ///  The enumerator must be disposed to release the underlying stack resources.
    /// </remarks>
    public ref struct Enumerator
    {
        private MemoryBuilder<GreenNode> _stack;
        private GreenNode? _current;
 
        public Enumerator(GreenNode node)
        {
            // MemoryBuilder<T> uses ArrayPool<T>.Shared to acquire an array.
            // So, we set an initial capacity of 256 to avoid unnecessary growth.
            // In addition, because the arrays used by MemoryBuilder<T> will be
            // returned to the pool, we clear them to ensure that GreenNodes aren't
            // kept in memory.
            _stack = new(initialCapacity: 256, clearArray: true);
            _stack.Push(node);
        }
 
        public void Dispose()
        {
            _stack.Dispose();
        }
 
        public readonly GreenNode Current => _current!;
 
        public bool MoveNext()
        {
            while (_stack.TryPop(out var node))
            {
                // Push children onto the stack in reverse order for correct traversal order.
                for (var i = node.SlotCount - 1; i >= 0; i--)
                {
                    var child = node.GetSlot(i);
                    if (child != null)
                    {
                        _stack.Push(child);
                    }
                }
 
                // If this is not a list node, return it as the current item.
                // List nodes are "transparent" - we push their children but don't return the list itself.
                if (!node.IsList)
                {
                    _current = node;
                    return true;
                }
            }
 
            // Stack is empty, no more nodes to process
            _current = null;
            return false;
        }
    }
}