File: Language\Syntax\SyntaxToken.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.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using Microsoft.Extensions.Internal;
 
namespace Microsoft.AspNetCore.Razor.Language.Syntax;
 
[DebuggerDisplay($"{{{nameof(GetDebuggerDisplay)}(), nq}}")]
internal readonly struct SyntaxToken : IEquatable<SyntaxToken>
{
    internal static readonly Func<SyntaxToken, bool> NonZeroWidth = t => t.Width > 0;
    internal static readonly Func<SyntaxToken, bool> Any = t => true;
 
    public SyntaxNode? Parent { get; }
    internal GreenNode? Node { get; }
    internal int Index { get; }
    internal int Position { get; }
 
    internal SyntaxToken(SyntaxNode? parent, GreenNode? token, int position, int index)
    {
        Debug.Assert(parent == null || !parent.Green.IsList, "list cannot be a parent");
        Debug.Assert(token == null || token.IsToken, "token must be a token");
 
        Parent = parent;
        Node = token;
        Position = position;
        Index = index;
    }
 
    internal SyntaxToken(GreenNode? token)
        : this()
    {
        Debug.Assert(token == null || token.IsToken, "token must be a token");
 
        Node = token;
    }
 
    private string GetDebuggerDisplay()
        => $"{GetType().Name} {Kind} {ToString()}";
 
    // For debugging
#pragma warning disable IDE0051 // Remove unused private members
    private string SerializedValue => SyntaxSerializer.Default.Serialize(this);
#pragma warning restore IDE0051 // Remove unused private members
    public SyntaxKind Kind => Node?.Kind ?? 0;
 
    internal GreenNode RequiredNode
    {
        get
        {
            Debug.Assert(Node != null);
            return Node;
        }
    }
 
    internal int Width => Node?.Width ?? 0;
 
    public int SpanStart
        => Node != null ? Position : 0;
 
    public TextSpan Span
        => Node != null ? new TextSpan(Position, Node.Width) : default;
 
    internal int EndPosition
        => Node != null ? Position + Node.Width : 0;
 
    public bool IsMissing
        => Node?.IsMissing ?? false;
 
    public bool ContainsDiagnostics => Node?.ContainsDiagnostics ?? false;
 
    public string Content => Node != null ? ((InternalSyntax.SyntaxToken)Node).Content : string.Empty;
 
    public string Text => ToString();
 
    public override string ToString()
        => Node?.ToString() ?? string.Empty;
 
    /// <summary>
    /// Determines whether two <see cref="SyntaxToken"/>s are equal.
    /// </summary>
    public static bool operator ==(SyntaxToken left, SyntaxToken right)
        => left.Equals(right);
 
    /// <summary>
    /// Determines whether two <see cref="SyntaxToken"/>s are unequal.
    /// </summary>
    public static bool operator !=(SyntaxToken left, SyntaxToken right)
        => !left.Equals(right);
 
    /// <summary>
    /// Determines whether the supplied <see cref="SyntaxToken"/> is equal to this
    /// <see cref="SyntaxToken"/>.
    /// </summary>
    public bool Equals(SyntaxToken other)
        => Parent == other.Parent &&
           Node == other.Node &&
           Position == other.Position &&
           Index == other.Index;
 
    public override bool Equals([NotNullWhen(true)] object? obj)
        => obj is SyntaxToken token && Equals(token);
 
    public override int GetHashCode()
    {
        var hash = HashCodeCombiner.Start();
        hash.Add(Parent);
        hash.Add(Node);
        hash.Add(Position);
        hash.Add(Index);
 
        return hash.CombinedHash;
    }
 
    /// <summary>
    /// Gets the token that follows this token in the syntax tree.
    /// </summary>
    /// <returns>The token that follows this token in the syntax tree.</returns>
    public SyntaxToken GetNextToken(bool includeZeroWidth = false)
    {
        if (Node == null)
        {
            return default;
        }
 
        return SyntaxNavigator.GetNextToken(this, includeZeroWidth);
    }
 
    /// <summary>
    /// Returns the token after this token in the syntax tree.
    /// </summary>
    /// <param name="predicate">Delegate applied to each token.  The token is returned if the predicate returns
    /// true.</param>
    internal SyntaxToken GetNextToken(Func<SyntaxToken, bool> predicate)
    {
        if (Node == null)
        {
            return default;
        }
 
        return SyntaxNavigator.GetNextToken(this, predicate);
    }
 
    /// <summary>
    /// Gets the token that precedes this token in the syntax tree.
    /// </summary>
    /// <returns>The previous token that precedes this token in the syntax tree.</returns>
    public SyntaxToken GetPreviousToken(bool includeZeroWidth = false)
    {
        if (Node == null)
        {
            return default;
        }
 
        return SyntaxNavigator.GetPreviousToken(this, includeZeroWidth);
    }
 
    /// <summary>
    /// Returns the token before this token in the syntax tree.
    /// </summary>
    /// <param name="predicate">Delegate applied to each token.  The token is returned if the predicate returns
    /// true.</param>
    internal SyntaxToken GetPreviousToken(Func<SyntaxToken, bool> predicate)
    {
        if (Node == null)
        {
            return default;
        }
 
        return SyntaxNavigator.GetPreviousToken(this, predicate);
    }
 
    /// <summary>
    /// Gets a list of all the diagnostics associated with this token and any related trivia.
    /// This method does not filter diagnostics based on #pragmas and compiler options
    /// like nowarn, warnaserror etc.
    /// </summary>
    public IEnumerable<RazorDiagnostic> GetDiagnostics()
    {
        if (Node == null)
        {
            return SpecializedCollections.EmptyEnumerable<RazorDiagnostic>();
        }
 
        var diagnostics = Node.GetDiagnostics();
 
        return diagnostics.Length == 0
            ? SpecializedCollections.EmptyEnumerable<RazorDiagnostic>()
            : diagnostics;
    }
}