450 lines
15 KiB
C#
Executable File
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.";
|
|
}
|
|
}
|
|
} |