File: src\Workspaces\SharedUtilitiesAndExtensions\Compiler\Core\EmbeddedLanguages\Common\EmbeddedSyntaxToken.cs
Web Access
Project: src\src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Text;
using Microsoft.CodeAnalysis.EmbeddedLanguages.VirtualChars;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Text;
 
namespace Microsoft.CodeAnalysis.EmbeddedLanguages.Common;
 
internal readonly struct EmbeddedSyntaxToken<TSyntaxKind> where TSyntaxKind : struct
{
    public readonly TSyntaxKind Kind;
    public readonly ImmutableArray<EmbeddedSyntaxTrivia<TSyntaxKind>> LeadingTrivia;
    public readonly VirtualCharSequence VirtualChars;
    public readonly ImmutableArray<EmbeddedSyntaxTrivia<TSyntaxKind>> TrailingTrivia;
    internal readonly ImmutableArray<EmbeddedDiagnostic> Diagnostics;
 
    /// <summary>
    /// Returns the value of the token. For example, if the token represents an integer capture,
    /// then this property would return the actual integer.
    /// </summary>
    public readonly object Value;
 
    public EmbeddedSyntaxToken(
        TSyntaxKind kind,
        ImmutableArray<EmbeddedSyntaxTrivia<TSyntaxKind>> leadingTrivia,
        VirtualCharSequence virtualChars,
        ImmutableArray<EmbeddedSyntaxTrivia<TSyntaxKind>> trailingTrivia,
        ImmutableArray<EmbeddedDiagnostic> diagnostics, object value)
    {
        Debug.Assert(!leadingTrivia.IsDefault);
        Debug.Assert(!virtualChars.IsDefault);
        Debug.Assert(!trailingTrivia.IsDefault);
        Debug.Assert(!diagnostics.IsDefault);
        Kind = kind;
        LeadingTrivia = leadingTrivia;
        VirtualChars = virtualChars;
        TrailingTrivia = trailingTrivia;
        Diagnostics = diagnostics;
        Value = value;
    }
 
    public bool IsMissing => VirtualChars.IsEmpty;
 
    public EmbeddedSyntaxToken<TSyntaxKind> AddDiagnosticIfNone(EmbeddedDiagnostic diagnostic)
        => Diagnostics.Length > 0 ? this : WithDiagnostics([diagnostic]);
 
    public EmbeddedSyntaxToken<TSyntaxKind> WithDiagnostics(ImmutableArray<EmbeddedDiagnostic> diagnostics)
        => With(diagnostics: diagnostics);
 
    public EmbeddedSyntaxToken<TSyntaxKind> With(
        Optional<TSyntaxKind> kind = default,
        Optional<ImmutableArray<EmbeddedSyntaxTrivia<TSyntaxKind>>> leadingTrivia = default,
        Optional<VirtualCharSequence> virtualChars = default,
        Optional<ImmutableArray<EmbeddedSyntaxTrivia<TSyntaxKind>>> trailingTrivia = default,
        Optional<ImmutableArray<EmbeddedDiagnostic>> diagnostics = default,
        Optional<object> value = default)
    {
        return new EmbeddedSyntaxToken<TSyntaxKind>(
            kind.HasValue ? kind.Value : Kind,
            leadingTrivia.HasValue ? leadingTrivia.Value : LeadingTrivia,
            virtualChars.HasValue ? virtualChars.Value : VirtualChars,
            trailingTrivia.HasValue ? trailingTrivia.Value : TrailingTrivia,
            diagnostics.HasValue ? diagnostics.Value : Diagnostics,
            value.HasValue ? value.Value : Value);
    }
 
    public TextSpan GetSpan()
        => EmbeddedSyntaxHelpers.GetSpan(this.VirtualChars);
 
    public TextSpan? GetFullSpan()
    {
        if (LeadingTrivia.Length == 0 && VirtualChars.Length == 0 && TrailingTrivia.Length == 0)
            return null;
 
        var start =
            Math.Min(LeadingTrivia.Length == 0 ? int.MaxValue : LeadingTrivia[0].GetSpan().Start,
            Math.Min(VirtualChars.Length == 0 ? int.MaxValue : VirtualChars[0].Span.Start,
                     TrailingTrivia.Length == 0 ? int.MaxValue : TrailingTrivia[0].GetSpan().Start));
 
        var end =
            Math.Max(LeadingTrivia.Length == 0 ? int.MinValue : LeadingTrivia[^1].GetSpan().End,
            Math.Max(VirtualChars.Length == 0 ? int.MinValue : VirtualChars[^1].Span.End,
                     TrailingTrivia.Length == 0 ? int.MinValue : TrailingTrivia[^1].GetSpan().End));
 
        return TextSpan.FromBounds(start, end);
    }
 
    public override string ToString()
    {
        using var _ = PooledStringBuilder.GetInstance(out var sb);
        WriteTo(sb, leading: false, trailing: false);
        return sb.ToString();
    }
 
    public string ToFullString()
    {
        using var _ = PooledStringBuilder.GetInstance(out var sb);
        WriteTo(sb, leading: true, trailing: true);
        return sb.ToString();
    }
 
    /// <summary>
    /// Writes the token 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, bool leading, bool trailing)
    {
        if (leading && !LeadingTrivia.IsDefault)
        {
            foreach (var trivia in LeadingTrivia)
            {
                sb.Append(trivia.ToString());
            }
        }
 
        sb.Append(VirtualChars.CreateString());
 
        if (trailing && !TrailingTrivia.IsDefault)
        {
            foreach (var trivia in TrailingTrivia)
            {
                sb.Append(trivia.ToString());
            }
        }
    }
}