// 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> AddDiagnosticIfMissing(EmbeddedDiagnostic diagnostic) => Diagnostics.Contains(diagnostic) ? this : With(diagnostics: this.Diagnostics.Add(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()); } } } } |