using Microsoft.AspNetCore.Mvc; using RoslynBridge.WebApi.Models; using RoslynBridge.WebApi.Services; namespace RoslynBridge.WebApi.Controllers; /// /// Controller for Roslyn code analysis operations /// [ApiController] [Route("api/[controller]")] [Produces("application/json")] public class RoslynController : ControllerBase { private readonly IRoslynBridgeClient _bridgeClient; private readonly ILogger _logger; public RoslynController(IRoslynBridgeClient bridgeClient, ILogger logger) { _bridgeClient = bridgeClient; _logger = logger; } /// /// Execute a Roslyn query /// /// The query request /// Optional: specific VS instance port to target /// Cancellation token /// The query result /// Query executed successfully /// Invalid request /// Internal server error [HttpPost("query")] [ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> ExecuteQuery( [FromBody] RoslynQueryRequest request, [FromQuery] int? instancePort = null, CancellationToken cancellationToken = default) { if (!ModelState.IsValid) { return BadRequest(ModelState); } _logger.LogInformation("Received query request: {QueryType}", request.QueryType); var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken); if (!result.Success) { _logger.LogWarning("Query failed: {Error}", result.Error); } return Ok(result); } /// /// Get all projects in the solution /// /// Optional: specific VS instance port to target /// Cancellation token /// List of projects [HttpGet("projects")] [ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)] public async Task> GetProjects( [FromQuery] int? instancePort = null, CancellationToken cancellationToken = default) { var request = new RoslynQueryRequest { QueryType = "getprojects" }; var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken); return Ok(result); } /// /// Get solution overview /// /// Optional: specific VS instance port to target /// Cancellation token /// Solution statistics and overview [HttpGet("solution/overview")] [ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)] public async Task> GetSolutionOverview( [FromQuery] int? instancePort = null, CancellationToken cancellationToken = default) { var request = new RoslynQueryRequest { QueryType = "getsolutionoverview" }; var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken); return Ok(result); } /// /// Get diagnostics (errors and warnings) /// /// Optional file path to filter diagnostics /// Optional: specific VS instance port to target /// Cancellation token /// List of diagnostics [HttpGet("diagnostics")] [ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)] public async Task> GetDiagnostics( [FromQuery] string? filePath = null, [FromQuery] int? instancePort = null, CancellationToken cancellationToken = default) { var request = new RoslynQueryRequest { QueryType = "getdiagnostics", FilePath = filePath }; var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken); return Ok(result); } /// /// Get symbol information at a specific position /// /// File path /// Line number (1-based) /// Column number (0-based) /// Optional: specific VS instance port to target /// Cancellation token /// Symbol information [HttpGet("symbol")] [ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)] public async Task> GetSymbol( [FromQuery] string filePath, [FromQuery] int line, [FromQuery] int column, [FromQuery] int? instancePort = null, CancellationToken cancellationToken = default) { var request = new RoslynQueryRequest { QueryType = "getsymbol", FilePath = filePath, Line = line, Column = column }; var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken); return Ok(result); } /// /// Find all references to a symbol /// /// File path /// Line number (1-based) /// Column number (0-based) /// Optional: specific VS instance port to target /// Cancellation token /// List of references [HttpGet("references")] [ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)] public async Task> FindReferences( [FromQuery] string filePath, [FromQuery] int line, [FromQuery] int column, [FromQuery] int? instancePort = null, CancellationToken cancellationToken = default) { var request = new RoslynQueryRequest { QueryType = "findreferences", FilePath = filePath, Line = line, Column = column }; var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken); return Ok(result); } /// /// Search for symbols by name /// /// Symbol name or pattern /// Optional symbol kind filter /// Optional: specific VS instance port to target /// Cancellation token /// List of matching symbols [HttpGet("symbol/search")] [ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)] public async Task> FindSymbol( [FromQuery] string symbolName, [FromQuery] string? kind = null, [FromQuery] int? instancePort = null, CancellationToken cancellationToken = default) { var request = new RoslynQueryRequest { QueryType = "findsymbol", SymbolName = symbolName }; if (!string.IsNullOrEmpty(kind)) { request.Parameters = new Dictionary { ["kind"] = kind }; } var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken); return Ok(result); } /// /// Format a document /// /// File path to format /// Optional: specific VS instance port to target /// Cancellation token /// Format operation result [HttpPost("format")] [ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)] public async Task> FormatDocument( [FromBody] string filePath, [FromQuery] int? instancePort = null, CancellationToken cancellationToken = default) { var request = new RoslynQueryRequest { QueryType = "formatdocument", FilePath = filePath }; var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken); return Ok(result); } /// /// Add a NuGet package to a project /// /// Project name /// NuGet package name /// Optional package version /// Optional: specific VS instance port to target /// Cancellation token /// Operation result [HttpPost("project/package/add")] [ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)] public async Task> AddNuGetPackage( [FromQuery] string projectName, [FromQuery] string packageName, [FromQuery] string? version = null, [FromQuery] int? instancePort = null, CancellationToken cancellationToken = default) { var request = new RoslynQueryRequest { QueryType = "addnugetpackage", ProjectName = projectName, PackageName = packageName, Version = version }; var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken); return Ok(result); } /// /// Build a project /// /// Project name /// Build configuration (Debug/Release) /// Optional: specific VS instance port to target /// Cancellation token /// Build result [HttpPost("project/build")] [ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)] public async Task> BuildProject( [FromQuery] string projectName, [FromQuery] string? configuration = null, [FromQuery] int? instancePort = null, CancellationToken cancellationToken = default) { var request = new RoslynQueryRequest { QueryType = "buildproject", ProjectName = projectName, Configuration = configuration }; var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken); return Ok(result); } }