using System; using System.Collections.Generic; using System.Text.RegularExpressions; using UnityEditor.Compilation; namespace io.github.hatayama.uLoopMCP { internal static class ExternalCompilerMessageParser { private static readonly Regex DiagnosticRegex = new( @"^(?.+)\((?\d+),(?\d+)\): (?error|warning) (?[A-Z]+\d+): (?.+)$", RegexOptions.Compiled); public static CompilerMessage[] Parse( string stdout, string stderr, int exitCode) { List messages = new List(); AddParsedMessages(messages, stdout); AddParsedMessages(messages, stderr); if (messages.Count > 0) { if (exitCode != 0 && !HasError(messages)) { messages.Add(CreateInfrastructureFailureMessage(stdout, stderr, exitCode)); } return messages.ToArray(); } if (exitCode == 0) { return Array.Empty(); } string combinedOutput = CombineOutput(stdout, stderr); return new CompilerMessage[] { new CompilerMessage { type = CompilerMessageType.Error, message = string.IsNullOrWhiteSpace(combinedOutput) ? "External C# compiler failed without diagnostics" : combinedOutput } }; } private static bool HasError(IReadOnlyCollection messages) { foreach (CompilerMessage message in messages) { if (message.type == CompilerMessageType.Error) { return true; } } return false; } private static CompilerMessage CreateInfrastructureFailureMessage( string stdout, string stderr, int exitCode) { string combinedOutput = CombineOutput(stdout, stderr); string message = $"External C# compiler exited with code {exitCode} without reporting an error diagnostic"; if (!string.IsNullOrWhiteSpace(combinedOutput)) { message = $"{message}: {combinedOutput}"; } return new CompilerMessage { type = CompilerMessageType.Error, message = message }; } private static void AddParsedMessages( List messages, string output) { if (string.IsNullOrWhiteSpace(output)) { return; } string[] lines = output.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (string line in lines) { Match match = DiagnosticRegex.Match(line.Trim()); if (!match.Success) { continue; } messages.Add(new CompilerMessage { type = match.Groups["severity"].Value == "warning" ? CompilerMessageType.Warning : CompilerMessageType.Error, message = $"{match.Groups["code"].Value}: {match.Groups["message"].Value}", file = match.Groups["file"].Value, line = int.Parse(match.Groups["line"].Value), column = int.Parse(match.Groups["column"].Value) }); } } private static string CombineOutput(string stdout, string stderr) { if (string.IsNullOrWhiteSpace(stdout)) { return stderr?.Trim(); } if (string.IsNullOrWhiteSpace(stderr)) { return stdout.Trim(); } return $"{stdout.Trim()}\n{stderr.Trim()}"; } } }