File: Language\SpanComputer.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.Diagnostics;
using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.CodeAnalysis.Text;
 
namespace Microsoft.AspNetCore.Razor.Language;
 
/// <summary>
///  Helper that can be used to efficiently build up a <see cref="SourceSpan"/> or
///  <see cref="CodeAnalysis.Text.TextSpan"/> from a set of syntax tokens.
/// </summary>
internal ref struct SpanComputer()
{
    private SyntaxToken _firstToken;
    private SyntaxToken _lastToken;
 
    public void Add(SyntaxToken token)
    {
        if (token.Kind == SyntaxKind.None)
        {
            return;
        }
 
        if (_firstToken.Kind == SyntaxKind.None)
        {
            _firstToken = token;
        }
 
        _lastToken = token;
    }
 
    public void Add(SyntaxTokenList tokenList)
    {
        if (tokenList.Count == 0)
        {
            return;
        }
 
        if (_firstToken.Kind == SyntaxKind.None)
        {
            _firstToken = tokenList[0];
        }
 
        _lastToken = tokenList[^1];
    }
 
    public void Add(SyntaxTokenList? tokenList)
    {
        if (tokenList is not [_, ..] tokens)
        {
            return;
        }
 
        if (_firstToken.Kind == SyntaxKind.None)
        {
            _firstToken = tokens[0];
        }
 
        _lastToken = tokens[^1];
    }
 
    public void Add(CSharpEphemeralTextLiteralSyntax? literal)
    {
        Add(literal?.LiteralTokens);
    }
 
    public void Add(CSharpExpressionLiteralSyntax? literal)
    {
        Add(literal?.LiteralTokens);
    }
 
    public void Add(CSharpStatementLiteralSyntax? literal)
    {
        Add(literal?.LiteralTokens);
    }
 
    public void Add(MarkupEphemeralTextLiteralSyntax? literal)
    {
        Add(literal?.LiteralTokens);
    }
 
    public void Add(MarkupTextLiteralSyntax? literal)
    {
        Add(literal?.LiteralTokens);
    }
 
    public void Add(UnclassifiedTextLiteralSyntax? literal)
    {
        Add(literal?.LiteralTokens);
    }
 
    public readonly SourceSpan ToSourceSpan(RazorSourceDocument source)
    {
        if (_firstToken.Kind == SyntaxKind.None)
        {
            return default;
        }
 
        Debug.Assert(_lastToken.Kind != SyntaxKind.None, "Last token should not be None when first token is set.");
 
        var start = _firstToken.Span.Start;
        var end = _lastToken.Span.End;
 
        Debug.Assert(start <= end, "Start position should not be greater than end position.");
 
        var length = end - start;
 
        var text = source.Text;
        var startLinePosition = text.Lines.GetLinePosition(start);
        var endLinePosition = text.Lines.GetLinePosition(end);
        var lineCount = endLinePosition.Line - startLinePosition.Line;
 
        return new SourceSpan(source.FilePath, absoluteIndex: start, startLinePosition.Line, startLinePosition.Character, length, lineCount, endLinePosition.Character);
    }
 
    public readonly TextSpan ToTextSpan()
    {
        if (_firstToken.Kind == SyntaxKind.None)
        {
            return default;
        }
 
        Debug.Assert(_lastToken.Kind != SyntaxKind.None, "Last token should not be None when first token is set.");
 
        var start = _firstToken.Span.Start;
        var end = _lastToken.Span.End;
 
        Debug.Assert(start <= end, "Start position should not be greater than end position.");
 
        return TextSpan.FromBounds(start, end);
    }
}