private/classes/PwshSpectreConsole.VTCodes.cs

using System;
using System.Collections.Generic;
using System.Management.Automation;

namespace PwshSpectreConsole.VTCodes
{
    public class VT
    {
        // objects of this class are returned by the Parser
        public class VtCode
        {
            public object Value { get; set; } // color, decoration, etc.
            public string Type { get; set; } // 4bit, 8bit, 24bit, decoration
            public string Position { get; set; } // foreground, background
            public int Placement { get; set; } // placement in the string
        }
        public class RGB
        {
            public int Red { get; set; }
            public int Green { get; set; }
            public int Blue { get; set; }
            public override string ToString()
            {
                return $"RGB({Red},{Green},{Blue})";
            }

        }
    }
    public static class DecorationDictionary
    {
        public static bool TryGetValue(int key, out string value)
        {
            if (DecorationDict.TryGetValue(key, out string str))
            {
                value = str;
                return true;
            }
            value = null;
            return false;
        }
        internal static Dictionary<int, string> DecorationDict { get; } = new Dictionary<int, string>()
        {
            { 0, "None" },
            { 1, "Bold" },
            { 2, "Dim" },
            { 3, "Italic" },
            { 4, "Underline" },
            { 5, "SlowBlink" },
            { 6, "RapidBlink" },
            { 7, "Invert" },
            { 8, "Conceal" },
            { 9, "Strikethrough" },
            { 21, "BoldOff" },
            { 22, "NormalIntensity" },
            { 23, "ItalicOff" },
            { 24, "UnderlineOff" },
            { 25, "BlinkOff" },
            { 27, "InvertOff" },
            { 28, "ConcealOff" },
            { 29, "StrikethroughOff" }
                // Add more entries as needed
        };
    }
    public class Parser
    {
        private static (string slice, int placement) GetNextSlice(ref ReadOnlySpan<char> inputSpan)
        {
            var escIndex = inputSpan.IndexOf('\x1B');
            if (escIndex == -1)
            {
                return (null, 0);
            }
            // Skip the '[' character after ESC
            var sliceStart = escIndex + 2;
            if (sliceStart >= inputSpan.Length)
            {
                return (null, 0);
            }
            var slice = inputSpan.Slice(sliceStart);
            var endIndex = slice.IndexOf('m');
            if (endIndex == -1)
            {
                return (null, 0);
            }
            var vtCode = slice.Slice(0, endIndex).ToString();
            var placement = sliceStart + endIndex - vtCode.Length;
            inputSpan = inputSpan.Slice(placement);
            return (vtCode, placement);
        }
        private static VT.VtCode New4BitVT(int firstCode, int placement)
        {
            string pos = (firstCode >= 30 && firstCode <= 37 || firstCode >= 90 && firstCode <= 97) ? "foreground" : "background";
            return new VT.VtCode
            {
                Value = firstCode,
                Type = "4bit",
                Position = pos,
                Placement = placement
            };
        }
        private static VT.VtCode New8BitVT(string[] codeParts, int placement, string position)
        {
            return new VT.VtCode
            {
                Value = int.Parse(codeParts[2]),
                Type = "8bit",
                Position = position,
                Placement = placement
            };
        }
        private static VT.VtCode New24BitVT(string[] codeParts, int placement, string position)
        {
            return new VT.VtCode
            {
                Value = new VT.RGB
                {
                    Red = int.Parse(codeParts[2]),
                    Green = int.Parse(codeParts[3]),
                    Blue = int.Parse(codeParts[4])
                },
                Type = "24bit",
                Position = position,
                Placement = placement
            };
        }
        private static VT.VtCode NewDecoVT(int firstCode, int placement)
        {
            if (DecorationDictionary.TryGetValue(firstCode, out string strDeco))
            {
                return new VT.VtCode
                {
                    Value = strDeco,
                    Type = "decoration",
                    Position = "",
                    Placement = placement
                };
            }
            return null;
        }
        private static VT.VtCode NewVT(int firstCode, string[] codeParts, int placement)
        {
            if (firstCode >= 30 && firstCode <= 37 || firstCode >= 40 && firstCode <= 47 || firstCode >= 90 && firstCode <= 97 || firstCode >= 100 && firstCode <= 107)
            {
                return New4BitVT(firstCode, placement);
            }
            else if (firstCode == 38 || firstCode == 48)
            {
                string position = firstCode == 48 ? "background" : "foreground";
                if (codeParts.Length >= 3 && codeParts[1] == "5")
                {
                    return New8BitVT(codeParts, placement, position);
                }
                else if (codeParts.Length >= 5 && codeParts[1] == "2")
                {
                    return New24BitVT(codeParts, placement, position);
                }
            }
            else
            {
                return NewDecoVT(firstCode, placement);
            }
            return null;
        }
        public static List<VT.VtCode> Parse(string input)
        {
            ReadOnlySpan<char> inputSpan = input.AsSpan();
            List<VT.VtCode> results = new List<VT.VtCode>();

            while (!inputSpan.IsEmpty)
            {
                var (slice, placement) = GetNextSlice(inputSpan: ref inputSpan);
                if (slice == null)
                {
                    break;
                }

                var codeParts = slice.Split(';');
                if (codeParts.Length > 0)
                {
                    try
                    {
                        int firstCode = int.Parse(codeParts[0]);
                        VT.VtCode _vtCode = NewVT(firstCode, codeParts, placement);
                        if (_vtCode != null)
                        {
                            results.Add(_vtCode);
                        }
                    }
                    catch (FormatException)
                    {
                        // Ignore
                    }
                }
            }
            return results;
        }
    }
}