using System.Text; namespace ExtractMethod.Tooling; /// /// Renders an as the human-readable extract- /// method suggestion report (parent spec): params (with ref), return /// candidates, locals, extract-first candidates (deduped, with occurrence /// counts and flags), the limitation notes, and the suggested signature line /// LAST. Pure string shaping — no Roslyn — so tests can pin the exact output /// the CLI prints. /// public static class ReportFormatter { public static string Format(ExtractionReport report) { var sb = new StringBuilder(); var selection = report.Selection; var suggestion = report.Suggestion; // Header (same wording the CLI printed since em 01). sb.AppendLine( $"{selection.Count} statement(s) selected, lines {selection.StartLine}..{selection.EndLine} " + $"in {selection.Method?.Identifier.ValueText}(): " + string.Join(", ", selection.Kinds)); AppendBucket(sb, "params", suggestion.Params.Select(p => $"{p.Name} ({p.Type}){(p.ByRef ? " [ref]" : " [in]")}")); AppendBucket(sb, "returns", suggestion.Returns.Select(r => $"{r.Name} ({r.Type})")); AppendBucket(sb, "locals", suggestion.Locals); if (report.ExtractFirst.Count == 0) { sb.AppendLine("extract-first: (none)"); } else { sb.AppendLine("extract-first:"); foreach (var entry in report.ExtractFirst) { // Flags accumulate; occurrence count is always shown because // "count occurrences" is the point of the dedupe. var flags = new List(); if (entry.IsInvocation) { flags.Add("hoisting changes eval count"); } if (entry.Optional) { flags.Add($"optional: own-class {entry.OwnMemberKind}"); } var flagText = flags.Count > 0 ? $" [{string.Join("; ", flags)}]" : string.Empty; sb.AppendLine($" - {entry.Text} — {entry.SymbolDisplay} ×{entry.Occurrences}{flagText}"); } } foreach (var note in report.Notes) { sb.AppendLine($"note: {note}"); } var signature = report.Signature; var parameterList = string.Join(", ", signature.Params.Select(p => $"{(p.ByRef ? "ref " : "")}{p.Type} {p.Name}")); sb.AppendLine($"suggested signature: {signature.ReturnType} {signature.MethodName}({parameterList})"); return sb.ToString(); } private static void AppendBucket(StringBuilder sb, string name, IEnumerable items) { var enumerated = items.ToList(); sb.AppendLine(enumerated.Count == 0 ? $"{name}: (none)" : $"{name}: {enumerated[0]}"); for (var i = 1; i < enumerated.Count; i++) { sb.AppendLine($"{name}: {enumerated[i]}"); } } }