MarketAlly.AIPlugin.Extensions/MarketAlly.AIPlugin.Refacto.../DocumentationServices.cs

450 lines
15 KiB
C#
Executable File

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MarketAlly.AIPlugin.Refactoring.Plugins
{
// Service for analyzing code structure
public interface ICodeAnalysisService
{
Task<CodeComplexityAnalysis> AnalyzeComplexityAsync(SyntaxTree syntaxTree);
Task<List<MethodInfo>> ExtractMethodsAsync(SyntaxTree syntaxTree);
Task<List<ClassInfo>> ExtractClassesAsync(SyntaxTree syntaxTree);
Task<QualityMetrics> CalculateQualityMetricsAsync(SyntaxTree syntaxTree);
}
// Service for generating documentation templates
public interface IDocumentationTemplateService
{
string GenerateClassDocumentation(ClassInfo classInfo, DocumentationOptions options);
string GenerateMethodDocumentation(MethodInfo methodInfo, DocumentationOptions options);
string GeneratePropertyDocumentation(PropertyInfo propertyInfo, DocumentationOptions options);
string GenerateFileHeader(string fileName, DocumentationOptions options);
}
// Service for AI integration
public interface IAIIntegrationService
{
Task<string> GenerateIntelligentDescriptionAsync(string codeContext, string apiKey);
Task<List<string>> SuggestImprovementsAsync(string codeContext, string apiKey);
Task<string> GenerateExampleUsageAsync(string methodSignature, string apiKey);
}
// Service for formatting documentation
public interface IDocumentationFormatter
{
string FormatAsXmlDocumentation(string content);
string FormatAsMarkdown(string content);
string FormatAsPlainText(string content);
string ApplyCodeFormattingRules(string content, FormattingStyle style);
}
// Data structures
public class CodeComplexityAnalysis
{
public int CyclomaticComplexity { get; set; }
public int LinesOfCode { get; set; }
public int NumberOfMethods { get; set; }
public int NumberOfClasses { get; set; }
public double MaintainabilityIndex { get; set; }
public List<ComplexityHotspot> Hotspots { get; set; } = new();
}
public class ComplexityHotspot
{
public string Name { get; set; }
public int Complexity { get; set; }
public int LineNumber { get; set; }
public string Reason { get; set; }
}
public class MethodInfo
{
public string Name { get; set; }
public string ReturnType { get; set; }
public List<ParameterInfo> Parameters { get; set; } = new();
public string AccessModifier { get; set; }
public bool IsStatic { get; set; }
public bool IsAsync { get; set; }
public int LineNumber { get; set; }
public int CyclomaticComplexity { get; set; }
public string ExistingDocumentation { get; set; }
}
public class ClassInfo
{
public string Name { get; set; }
public string Namespace { get; set; }
public string AccessModifier { get; set; }
public bool IsAbstract { get; set; }
public bool IsStatic { get; set; }
public List<string> Interfaces { get; set; } = new();
public string BaseClass { get; set; }
public List<MethodInfo> Methods { get; set; } = new();
public List<PropertyInfo> Properties { get; set; } = new();
public int LineNumber { get; set; }
public string ExistingDocumentation { get; set; }
}
public class PropertyInfo
{
public string Name { get; set; }
public string Type { get; set; }
public string AccessModifier { get; set; }
public bool HasGetter { get; set; }
public bool HasSetter { get; set; }
public bool IsStatic { get; set; }
public int LineNumber { get; set; }
public string ExistingDocumentation { get; set; }
}
public class ParameterInfo
{
public string Name { get; set; }
public string Type { get; set; }
public bool HasDefaultValue { get; set; }
public string DefaultValue { get; set; }
public bool IsParams { get; set; }
}
public class QualityMetrics
{
public double CohesionScore { get; set; }
public double CouplingScore { get; set; }
public double ComplexityScore { get; set; }
public double DocumentationCoverage { get; set; }
public int CodeSmells { get; set; }
public List<string> Suggestions { get; set; } = new();
}
public class DocumentationOptions
{
public bool IncludeParameters { get; set; } = true;
public bool IncludeReturns { get; set; } = true;
public bool IncludeExceptions { get; set; } = true;
public bool IncludeExamples { get; set; } = false;
public bool IncludeRemarks { get; set; } = false;
public bool GenerateAIDescriptions { get; set; } = false;
public DocumentationStyle Style { get; set; } = DocumentationStyle.Standard;
public string Author { get; set; } = "Generated";
public DateTime CreatedDate { get; set; } = DateTime.UtcNow;
}
public enum DocumentationStyle
{
Standard,
Detailed,
Minimal,
Enterprise
}
public enum FormattingStyle
{
Microsoft,
Allman,
KAndR,
Google
}
// Concrete implementations
public class CodeAnalysisService : ICodeAnalysisService
{
public async Task<CodeComplexityAnalysis> AnalyzeComplexityAsync(SyntaxTree syntaxTree)
{
var root = await syntaxTree.GetRootAsync();
var analysis = new CodeComplexityAnalysis();
// Count methods and classes
var methods = root.DescendantNodes().OfType<MethodDeclarationSyntax>().ToList();
var classes = root.DescendantNodes().OfType<ClassDeclarationSyntax>().ToList();
analysis.NumberOfMethods = methods.Count;
analysis.NumberOfClasses = classes.Count;
analysis.LinesOfCode = syntaxTree.GetText().Lines.Count;
// Calculate cyclomatic complexity
foreach (var method in methods)
{
var complexity = CalculateMethodComplexity(method);
analysis.CyclomaticComplexity += complexity;
if (complexity > 10)
{
analysis.Hotspots.Add(new ComplexityHotspot
{
Name = method.Identifier.ValueText,
Complexity = complexity,
LineNumber = method.GetLocation().GetLineSpan().StartLinePosition.Line + 1,
Reason = "High cyclomatic complexity"
});
}
}
// Calculate maintainability index (simplified)
analysis.MaintainabilityIndex = CalculateMaintainabilityIndex(analysis);
return analysis;
}
public async Task<List<MethodInfo>> ExtractMethodsAsync(SyntaxTree syntaxTree)
{
var root = await syntaxTree.GetRootAsync();
var methods = new List<MethodInfo>();
foreach (var method in root.DescendantNodes().OfType<MethodDeclarationSyntax>())
{
var methodInfo = new MethodInfo
{
Name = method.Identifier.ValueText,
ReturnType = method.ReturnType.ToString(),
AccessModifier = GetAccessModifier(method.Modifiers),
IsStatic = method.Modifiers.Any(m => m.IsKind(SyntaxKind.StaticKeyword)),
IsAsync = method.Modifiers.Any(m => m.IsKind(SyntaxKind.AsyncKeyword)),
LineNumber = method.GetLocation().GetLineSpan().StartLinePosition.Line + 1,
CyclomaticComplexity = CalculateMethodComplexity(method),
ExistingDocumentation = ExtractDocumentation(method)
};
// Extract parameters
foreach (var param in method.ParameterList.Parameters)
{
methodInfo.Parameters.Add(new ParameterInfo
{
Name = param.Identifier.ValueText,
Type = param.Type?.ToString() ?? "unknown",
HasDefaultValue = param.Default != null,
DefaultValue = param.Default?.Value?.ToString(),
IsParams = param.Modifiers.Any(m => m.IsKind(SyntaxKind.ParamsKeyword))
});
}
methods.Add(methodInfo);
}
return methods;
}
public async Task<List<ClassInfo>> ExtractClassesAsync(SyntaxTree syntaxTree)
{
var root = await syntaxTree.GetRootAsync();
var classes = new List<ClassInfo>();
foreach (var classDeclaration in root.DescendantNodes().OfType<ClassDeclarationSyntax>())
{
var classInfo = new ClassInfo
{
Name = classDeclaration.Identifier.ValueText,
Namespace = GetNamespace(classDeclaration),
AccessModifier = GetAccessModifier(classDeclaration.Modifiers),
IsAbstract = classDeclaration.Modifiers.Any(m => m.IsKind(SyntaxKind.AbstractKeyword)),
IsStatic = classDeclaration.Modifiers.Any(m => m.IsKind(SyntaxKind.StaticKeyword)),
LineNumber = classDeclaration.GetLocation().GetLineSpan().StartLinePosition.Line + 1,
ExistingDocumentation = ExtractDocumentation(classDeclaration)
};
// Extract base class and interfaces
if (classDeclaration.BaseList != null)
{
foreach (var baseType in classDeclaration.BaseList.Types)
{
var typeName = baseType.Type.ToString();
if (baseType.Type is IdentifierNameSyntax && char.IsUpper(typeName[0]))
{
if (classInfo.BaseClass == null)
classInfo.BaseClass = typeName;
else
classInfo.Interfaces.Add(typeName);
}
else
{
classInfo.Interfaces.Add(typeName);
}
}
}
classes.Add(classInfo);
}
return classes;
}
public async Task<QualityMetrics> CalculateQualityMetricsAsync(SyntaxTree syntaxTree)
{
var analysis = await AnalyzeComplexityAsync(syntaxTree);
var methods = await ExtractMethodsAsync(syntaxTree);
var classes = await ExtractClassesAsync(syntaxTree);
var metrics = new QualityMetrics();
// Calculate documentation coverage
var documntedMethods = methods.Count(m => !string.IsNullOrEmpty(m.ExistingDocumentation));
var documentedClasses = classes.Count(c => !string.IsNullOrEmpty(c.ExistingDocumentation));
if (methods.Count + classes.Count > 0)
{
metrics.DocumentationCoverage = (double)(documntedMethods + documentedClasses) / (methods.Count + classes.Count);
}
// Calculate complexity score (normalized)
metrics.ComplexityScore = Math.Min(1.0, analysis.CyclomaticComplexity / 100.0);
// Identify code smells
metrics.CodeSmells = analysis.Hotspots.Count;
metrics.CodeSmells += methods.Count(m => m.Parameters.Count > 5); // Too many parameters
metrics.CodeSmells += classes.Count(c => c.Methods.Count > 20); // God classes
// Generate suggestions
if (metrics.DocumentationCoverage < 0.5)
metrics.Suggestions.Add("Consider adding more documentation to improve code maintainability");
if (analysis.Hotspots.Any())
metrics.Suggestions.Add("Some methods have high complexity and could benefit from refactoring");
return metrics;
}
private int CalculateMethodComplexity(MethodDeclarationSyntax method)
{
int complexity = 1; // Base complexity
// Count decision points
var decisionNodes = method.DescendantNodes().Where(node =>
node.IsKind(SyntaxKind.IfStatement) ||
node.IsKind(SyntaxKind.WhileStatement) ||
node.IsKind(SyntaxKind.ForStatement) ||
node.IsKind(SyntaxKind.ForEachStatement) ||
node.IsKind(SyntaxKind.SwitchStatement) ||
node.IsKind(SyntaxKind.CatchClause) ||
node.IsKind(SyntaxKind.ConditionalExpression));
complexity += decisionNodes.Count();
return complexity;
}
private double CalculateMaintainabilityIndex(CodeComplexityAnalysis analysis)
{
// Simplified maintainability index calculation
var volume = analysis.LinesOfCode * Math.Log2(Math.Max(1, analysis.NumberOfMethods));
var complexity = Math.Max(1, analysis.CyclomaticComplexity);
return Math.Max(0, (171 - 5.2 * Math.Log(volume) - 0.23 * complexity - 16.2 * Math.Log(analysis.LinesOfCode)) * 100 / 171);
}
private string GetAccessModifier(SyntaxTokenList modifiers)
{
if (modifiers.Any(m => m.IsKind(SyntaxKind.PublicKeyword)))
return "public";
if (modifiers.Any(m => m.IsKind(SyntaxKind.PrivateKeyword)))
return "private";
if (modifiers.Any(m => m.IsKind(SyntaxKind.ProtectedKeyword)))
return "protected";
if (modifiers.Any(m => m.IsKind(SyntaxKind.InternalKeyword)))
return "internal";
return "private"; // Default in C#
}
private string GetNamespace(SyntaxNode node)
{
var namespaceDeclaration = node.Ancestors().OfType<NamespaceDeclarationSyntax>().FirstOrDefault();
return namespaceDeclaration?.Name?.ToString() ?? "";
}
private string ExtractDocumentation(SyntaxNode node)
{
var documentationComment = node.GetLeadingTrivia()
.FirstOrDefault(t => t.IsKind(SyntaxKind.SingleLineDocumentationCommentTrivia) ||
t.IsKind(SyntaxKind.MultiLineDocumentationCommentTrivia));
return documentationComment.ToString().Trim();
}
}
public class DocumentationTemplateService : IDocumentationTemplateService
{
public string GenerateClassDocumentation(ClassInfo classInfo, DocumentationOptions options)
{
var sb = new StringBuilder();
sb.AppendLine("/// <summary>");
sb.AppendLine($"/// Represents a {classInfo.Name} class.");
sb.AppendLine("/// </summary>");
if (options.IncludeRemarks)
{
sb.AppendLine("/// <remarks>");
sb.AppendLine($"/// This class contains {classInfo.Methods.Count} methods and {classInfo.Properties.Count} properties.");
sb.AppendLine("/// </remarks>");
}
return sb.ToString();
}
public string GenerateMethodDocumentation(MethodInfo methodInfo, DocumentationOptions options)
{
var sb = new StringBuilder();
sb.AppendLine("/// <summary>");
sb.AppendLine($"/// {GenerateMethodDescription(methodInfo)}");
sb.AppendLine("/// </summary>");
if (options.IncludeParameters && methodInfo.Parameters.Any())
{
foreach (var param in methodInfo.Parameters)
{
sb.AppendLine($"/// <param name=\"{param.Name}\">The {param.Name} parameter.</param>");
}
}
if (options.IncludeReturns && methodInfo.ReturnType != "void")
{
sb.AppendLine($"/// <returns>Returns a {methodInfo.ReturnType}.</returns>");
}
return sb.ToString();
}
public string GeneratePropertyDocumentation(PropertyInfo propertyInfo, DocumentationOptions options)
{
var sb = new StringBuilder();
sb.AppendLine("/// <summary>");
sb.AppendLine($"/// Gets or sets the {propertyInfo.Name}.");
sb.AppendLine("/// </summary>");
return sb.ToString();
}
public string GenerateFileHeader(string fileName, DocumentationOptions options)
{
var sb = new StringBuilder();
sb.AppendLine("/*");
sb.AppendLine($" * File: {fileName}");
sb.AppendLine($" * Generated: {options.CreatedDate:yyyy-MM-dd HH:mm:ss}");
sb.AppendLine($" * Author: {options.Author}");
sb.AppendLine(" */");
sb.AppendLine();
return sb.ToString();
}
private string GenerateMethodDescription(MethodInfo methodInfo)
{
if (methodInfo.Name.StartsWith("Get"))
return $"Gets {methodInfo.Name.Substring(3).ToLowerInvariant()} information.";
if (methodInfo.Name.StartsWith("Set"))
return $"Sets {methodInfo.Name.Substring(3).ToLowerInvariant()} information.";
if (methodInfo.Name.StartsWith("Create"))
return $"Creates a new {methodInfo.Name.Substring(6).ToLowerInvariant()}.";
if (methodInfo.Name.StartsWith("Delete"))
return $"Deletes the specified {methodInfo.Name.Substring(6).ToLowerInvariant()}.";
if (methodInfo.Name.StartsWith("Update"))
return $"Updates the specified {methodInfo.Name.Substring(6).ToLowerInvariant()}.";
return $"Executes the {methodInfo.Name} operation.";
}
}
}