File: src\Workspaces\SharedUtilitiesAndExtensions\Compiler\Core\EmbeddedLanguages\VirtualChars\VirtualCharGreenSequence.Chunks.cs
Web Access
Project: src\src\RoslynAnalyzers\Tools\Metrics.Legacy\Metrics.Legacy.csproj (Metrics.Legacy)
// 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.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.Collections;
 
namespace Microsoft.CodeAnalysis.EmbeddedLanguages.VirtualChars;
 
internal readonly partial struct VirtualCharGreenSequence
{
    /// <summary>
    /// Abstraction over a contiguous chunk of <see cref="VirtualChar"/>s.  This
    /// is used so we can expose <see cref="VirtualChar"/>s over an <see cref="ImmutableArray{VirtualChar}"/>
    /// or over a <see cref="string"/>.  The latter is especially useful for reducing
    /// memory usage in common cases of string tokens without escapes.
    /// </summary>
    private abstract partial class Chunk
    {
        public abstract int Length { get; }
        public abstract VirtualCharGreen this[int index] { get; }
        public abstract int? FindIndex(int tokenStart, int position);
    }
 
    /// <summary>
    /// Thin wrapper over an actual <see cref="ImmutableSegmentedList{T}"/>.
    /// This will be the common construct we generate when getting the
    /// <see cref="Chunk"/> for a string token that has escapes in it.
    /// </summary>
    private sealed class ImmutableSegmentedListChunk(ImmutableSegmentedList<VirtualCharGreen> array) : Chunk
    {
        public override int Length => array.Count;
        public override VirtualCharGreen this[int index] => array[index];
 
        public override int? FindIndex(int tokenStart, int position)
        {
            if (array.IsEmpty)
                return null;
 
            if (position < new VirtualChar(array[0], tokenStart).Span.Start ||
                position >= new VirtualChar(array[^1], tokenStart).Span.End)
            {
                return null;
            }
 
            var index = array.BinarySearch((tokenStart, position), static (ch, tuple) =>
            {
                var (tokenStart, position) = tuple;
                var span = new VirtualChar(ch, tokenStart).Span;
 
                if (position < span.Start)
                    return 1;
 
                if (position >= span.End)
                    return -1;
 
                return 0;
            });
 
            // Characters can be discontinuous (for example, in multi-line-raw-string literals).  So if the position is
            // in one of the gaps, it won't be able to find a corresponding virtual char.
            return index < 0 ? null : index;
        }
    }
 
    /// <summary>
    /// Represents a <see cref="Chunk"/> on top of a normal string.  This is the common case of the type of the sequence
    /// we would create for a normal string token without any escapes in it.
    /// </summary>
    /// <param name="data">
    /// The underlying string that we're returning virtual chars from.  Note: this will commonly include things like
    /// quote characters.  Clients who do not want that should then ask for an appropriate <see
    /// cref="VirtualCharSequence.Slice"/> back that does not include those characters.
    /// </param>
    private sealed class StringChunk(string data) : Chunk
    {
        public override int Length => data.Length;
 
        public override int? FindIndex(int tokenStart, int position)
        {
            var stringIndex = position - tokenStart;
            return stringIndex >= 0 && stringIndex < data.Length ? stringIndex : null;
        }
 
        public override VirtualCharGreen this[int index]
            => new(data[index], offset: index, width: 1);
    }
}