diff --git a/RoslynBridge.WebApi/Controllers/RoslynController.cs b/RoslynBridge.WebApi/Controllers/RoslynController.cs index dc4c630..f98223e 100644 --- a/RoslynBridge.WebApi/Controllers/RoslynController.cs +++ b/RoslynBridge.WebApi/Controllers/RoslynController.cs @@ -1,5 +1,8 @@ using Microsoft.AspNetCore.Mvc; using RoslynBridge.WebApi.Models; +using System.Text; +using System.Text.Json; +using System.Linq; using RoslynBridge.WebApi.Services; namespace RoslynBridge.WebApi.Controllers; @@ -91,6 +94,130 @@ public class RoslynController : ControllerBase return Ok(result); } + /// + /// Get a compact summary of the solution overview + /// + /// How many projects to list (by file count) + /// Include top-level namespaces in summary + /// One of: json | text | yaml + /// Optional: specific VS instance port to target + /// Cancellation token + /// Compact summary as JSON or plain text + [HttpGet("solution/overview/summary")] + [ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)] + public async Task GetSolutionOverviewSummary( + [FromQuery] int topNProjects = 5, + [FromQuery] bool includeNamespaces = true, + [FromQuery] string? format = "json", + [FromQuery] int? instancePort = null, + CancellationToken cancellationToken = default) + { + var request = new RoslynQueryRequest { QueryType = "getsolutionoverview" }; + var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken); + + if (!result.Success) + { + if (string.Equals(format, "json", StringComparison.OrdinalIgnoreCase)) + { + return Ok(result); + } + var err = result.Error ?? "Unknown error"; + return Content($"error: {err}", "text/plain", Encoding.UTF8); + } + + // Expect Data as JsonElement; shape per Models.SolutionOverview + int projectCount = 0; + int documentCount = 0; + var projectsSummary = new List<(string Name, int FileCount)>(); + var topLevelNamespaces = new List(); + + if (result.Data is JsonElement dataEl && dataEl.ValueKind == JsonValueKind.Object) + { + if (dataEl.TryGetProperty("projectCount", out var pc)) projectCount = pc.GetInt32(); + if (dataEl.TryGetProperty("documentCount", out var dc)) documentCount = dc.GetInt32(); + if (includeNamespaces && dataEl.TryGetProperty("topLevelNamespaces", out var nsEl) && nsEl.ValueKind == JsonValueKind.Array) + { + foreach (var ns in nsEl.EnumerateArray()) + { + if (ns.ValueKind == JsonValueKind.String) + { + topLevelNamespaces.Add(ns.GetString() ?? string.Empty); + } + } + } + if (dataEl.TryGetProperty("projects", out var prEl) && prEl.ValueKind == JsonValueKind.Array) + { + foreach (var p in prEl.EnumerateArray()) + { + string name = p.TryGetProperty("name", out var nEl) && nEl.ValueKind == JsonValueKind.String ? (nEl.GetString() ?? string.Empty) : string.Empty; + int files = p.TryGetProperty("fileCount", out var fEl) && fEl.ValueKind == JsonValueKind.Number ? fEl.GetInt32() : 0; + projectsSummary.Add((name, files)); + } + } + } + + var topProjects = projectsSummary + .OrderByDescending(p => p.FileCount) + .ThenBy(p => p.Name) + .Take(Math.Max(0, topNProjects)) + .ToList(); + + if (string.Equals(format, "json", StringComparison.OrdinalIgnoreCase)) + { + var compact = new + { + projectCount, + documentCount, + projects = topProjects.Select(p => new { name = p.Name, files = p.FileCount }).ToList(), + topLevelNamespaces = includeNamespaces ? topLevelNamespaces : null + }; + + return Ok(new RoslynQueryResponse + { + Success = true, + Message = "Compact solution overview", + Data = compact, + Error = null + }); + } + + // text or yaml: render a compact string + var sb = new StringBuilder(); + if (string.Equals(format, "yaml", StringComparison.OrdinalIgnoreCase)) + { + sb.AppendLine($"projectCount: {projectCount}"); + sb.AppendLine($"documentCount: {documentCount}"); + if (includeNamespaces) + { + sb.Append("topLevelNamespaces: ["); + sb.Append(string.Join(", ", topLevelNamespaces)); + sb.AppendLine("]"); + } + sb.AppendLine("projects:"); + foreach (var p in topProjects) + { + sb.AppendLine($" - name: {p.Name}"); + sb.AppendLine($" files: {p.FileCount}"); + } + } + else + { + // text + sb.AppendLine($"Projects: {projectCount} | Documents: {documentCount}"); + if (includeNamespaces && topLevelNamespaces.Count > 0) + { + sb.AppendLine($"TopNamespaces: {string.Join(", ", topLevelNamespaces)}"); + } + sb.AppendLine("Top Projects:"); + foreach (var p in topProjects) + { + sb.AppendLine($" - {p.Name} ({p.FileCount})"); + } + } + + return Content(sb.ToString(), "text/plain", Encoding.UTF8); + } + /// /// Get diagnostics (errors and warnings) ///