| File: Infrastructure\EmbeddedSyntax\EmbeddedSyntaxNode.cs | Web Access |
| Project: src\aspnetcore\src\Framework\AspNetCoreAnalyzers\src\Analyzers\Microsoft.AspNetCore.App.Analyzers.csproj (Microsoft.AspNetCore.App.Analyzers) |
// 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 System.Text; using Microsoft.AspNetCore.Analyzers.Infrastructure.VirtualChars; using Microsoft.CodeAnalysis.Text; namespace Microsoft.AspNetCore.Analyzers.Infrastructure.EmbeddedSyntax; /// <summary> /// Root of the embedded language syntax hierarchy. EmbeddedSyntaxNodes are very similar to /// Roslyn Red-Nodes in concept, though there are differences for ease of implementation. /// </summary> internal abstract class EmbeddedSyntaxNode<TSyntaxKind, TSyntaxNode> where TSyntaxKind : struct where TSyntaxNode : EmbeddedSyntaxNode<TSyntaxKind, TSyntaxNode> { public readonly TSyntaxKind Kind; private TextSpan? _fullSpan; protected EmbeddedSyntaxNode(TSyntaxKind kind) { Debug.Assert((int)(object)kind != 0); Kind = kind; } internal abstract int ChildCount { get; } internal abstract EmbeddedSyntaxNodeOrToken<TSyntaxKind, TSyntaxNode> ChildAt(int index); public EmbeddedSyntaxNodeOrToken<TSyntaxKind, TSyntaxNode> this[int index] => ChildAt(index); public TextSpan GetSpan() { var start = int.MaxValue; var end = 0; GetSpan(ref start, ref end); return TextSpan.FromBounds(start, end); } public TextSpan? GetFullSpan() => _fullSpan ??= ComputeFullSpan(); private TextSpan? ComputeFullSpan() { var start = ComputeStart(); var end = ComputeEnd(); if (start == null || end == null) { return null; } return TextSpan.FromBounds(start.Value, end.Value); int? ComputeStart() { for (int i = 0, n = ChildCount; i < n; i++) { var child = ChildAt(i); var span = child.GetFullSpan(); if (span != null) { return span.Value.Start; } } return null; } int? ComputeEnd() { for (var i = ChildCount - 1; i >= 0; i--) { var child = ChildAt(i); var span = child.GetFullSpan(); if (span != null) { return span.Value.End; } } return null; } } private void GetSpan(ref int start, ref int end) { foreach (var child in this) { if (child.IsNode) { child.Node.GetSpan(ref start, ref end); } else { var token = child.Token; if (!token.IsMissing) { start = Math.Min(token.VirtualChars[0].Span.Start, start); end = Math.Max(token.VirtualChars.Last().Span.End, end); } } } } public bool Contains(VirtualChar virtualChar) { foreach (var child in this) { if (child.IsNode) { if (child.Node.Contains(virtualChar)) { return true; } } else { if (child.Token.VirtualChars.Contains(virtualChar)) { return true; } } } return false; } /// <summary> /// Returns the string representation of this node, not including its leading and trailing trivia. /// </summary> /// <returns>The string representation of this node, not including its leading and trailing trivia.</returns> /// <remarks>The length of the returned string is always the same as Span.Length</remarks> public override string ToString() { var sb = new StringBuilder(); WriteTo(sb); return sb.ToString(); } /// <summary> /// Returns full string representation of this node including its leading and trailing trivia. /// </summary> /// <returns>The full string representation of this node including its leading and trailing trivia.</returns> /// <remarks>The length of the returned string is always the same as FullSpan.Length</remarks> public string ToFullString() { var sb = new StringBuilder(); WriteTo(sb); return sb.ToString(); } /// <summary> /// Writes the node to a stringbuilder. /// </summary> /// <param name="leading">If false, leading trivia will not be added</param> /// <param name="trailing">If false, trailing trivia will not be added</param> public void WriteTo(StringBuilder sb) { for (var i = 0; i < ChildCount; i++) { var child = this[i]; if (child.IsNode) { child.Node.WriteTo(sb); } else { child.Token.WriteTo(sb); } } } public Enumerator GetEnumerator() => new(this); public struct Enumerator { private readonly EmbeddedSyntaxNode<TSyntaxKind, TSyntaxNode> _node; private readonly int _childCount; private int _currentIndex; public Enumerator(EmbeddedSyntaxNode<TSyntaxKind, TSyntaxNode> node) { _node = node; _childCount = _node.ChildCount; _currentIndex = -1; Current = default; } public EmbeddedSyntaxNodeOrToken<TSyntaxKind, TSyntaxNode> Current { get; private set; } public bool MoveNext() { _currentIndex++; if (_currentIndex >= _childCount) { Current = default; return false; } Current = _node.ChildAt(_currentIndex); return true; } } }