using System.Text; using syntax.Tokenization; namespace syntax.Diagnostics; public static class ConsoleColors { public const string Reset = "\e[0m"; public const string Bold = "\e[1m"; public const string Faint = "\e[2m"; public const string Italic = "\e[3m"; public const string Underline = "\e[4m"; public const string SlowBlink = "\e[5m"; public const string RapidBlink = "\e[6m"; public const string SwapBgAndFg = "\e[7m"; public const string Conceal = "\e[8m"; public const string CrossedOut = "\e[9m"; public const string DefaultFont = "\e[10m"; public const string AltFont1 = "\e[11m"; public const string AltFont2 = "\e[12m"; public const string AltFont3 = "\e[13m"; public const string AltFont4 = "\e[14m"; public const string AltFont5 = "\e[15m"; public const string AltFont6 = "\e[16m"; public const string AltFont7 = "\e[17m"; public const string AltFont8 = "\e[18m"; public const string AltFont9 = "\e[19m"; public const string Black = "\e[30m"; public const string Red = "\e[31m"; public const string Green = "\e[32m"; public const string Yellow = "\e[33m"; public const string Blue = "\e[34m"; public const string Magenta = "\e[35m"; public const string Cyan = "\e[36m"; public const string White = "\e[37m"; public const string BrightBlack = "\e[90m"; public const string BrightRed = "\e[91m"; public const string BrightGreen = "\e[92m"; public const string BrightYellow = "\e[93m"; public const string BrightBlue = "\e[94m"; public const string BrightMagenta = "\e[95m"; public const string BrightCyan = "\e[96m"; public const string BrightWhite = "\e[97m"; private static bool IsColorSupported() { var term = Environment.GetEnvironmentVariable("TERM"); var colorTerm = Environment.GetEnvironmentVariable("COLORTERM"); return !string.IsNullOrEmpty(term) || !string.IsNullOrEmpty(colorTerm) || !Console.IsOutputRedirected; } public static string Colorize(string text, string color) { return IsColorSupported() ? $"{color}{text}{Reset}" : text; } private static string GetTokenColor(Token token) { switch (token) { case DocumentationToken: return Faint; case IdentifierToken: return White; case LiteralToken literal: return literal.Kind switch { LiteralKind.String => Green, LiteralKind.Integer or LiteralKind.Float => BrightBlue, LiteralKind.Bool => Blue, _ => White }; case ModifierToken: return White; case SymbolToken symbol: switch (symbol.Symbol) { case Symbol.If: case Symbol.Else: case Symbol.While: case Symbol.Break: case Symbol.Continue: case Symbol.Return: return Magenta; case Symbol.Func: case Symbol.Struct: case Symbol.Namespace: case Symbol.Let: case Symbol.Alloc: return Blue; case Symbol.Assign: case Symbol.Bang: case Symbol.Equal: case Symbol.NotEqual: case Symbol.LessThan: case Symbol.LessThanOrEqual: case Symbol.GreaterThan: case Symbol.GreaterThanOrEqual: case Symbol.Plus: case Symbol.Minus: case Symbol.Star: case Symbol.ForwardSlash: case Symbol.Caret: case Symbol.Ampersand: return White; case Symbol.Semicolon: case Symbol.Colon: case Symbol.Comma: case Symbol.Period: case Symbol.DoubleColon: return Faint; case Symbol.OpenParen: case Symbol.CloseParen: case Symbol.OpenBrace: case Symbol.CloseBrace: case Symbol.OpenBracket: case Symbol.CloseBracket: return Yellow; default: return White; } default: return White; } } public static string ColorizeSource(string source) { var sourceText = new SourceText(string.Empty, source); var tokens = Tokenizer.Tokenize(sourceText).Value; var result = new StringBuilder(); var lastCharIndex = 0; foreach (var token in tokens) { var tokenStartIndex = GetCharacterIndex(sourceText, token.Span.Start); var tokenEndIndex = GetCharacterIndex(sourceText, token.Span.End); if (tokenStartIndex > lastCharIndex) { var between = sourceText.Content.Substring(lastCharIndex, tokenStartIndex - lastCharIndex); result.Append(between); } var tokenText = sourceText.Content.Substring(tokenStartIndex, tokenEndIndex - tokenStartIndex); result.Append(Colorize(tokenText, GetTokenColor(token))); lastCharIndex = tokenEndIndex; } if (lastCharIndex < sourceText.Content.Length) { var remaining = sourceText.Content[lastCharIndex..]; result.Append(Colorize(remaining, Faint)); } return result.ToString(); } private static int GetCharacterIndex(SourceText sourceText, SourceLocation location) { var lines = sourceText.Content.Split('\n'); var index = 0; for (var i = 0; i < location.Line - 1 && i < lines.Length; i++) { index += lines[i].Length + 1; } index += location.Column - 1; return Math.Min(index, sourceText.Content.Length); } }