Primera version funcionante
This commit is contained in:
parent
641bb7baf1
commit
ebbee5414d
|
@ -20,7 +20,7 @@ namespace NetDocsForLLM
|
|||
private void ConfigureServices(ServiceCollection services)
|
||||
{
|
||||
// Register services
|
||||
services.AddSingleton<IDocFxService, ReflectionAnalyzerService>(); // <- Cambio aquí
|
||||
services.AddSingleton<IDocFxService, XmlDocGenerator>(); // Usar el generador XML
|
||||
services.AddSingleton<IDocumentationGenerator, DocumentationGenerator>();
|
||||
services.AddSingleton<IAssemblyAnalyzer, AssemblyAnalyzer>();
|
||||
|
||||
|
|
|
@ -162,21 +162,16 @@
|
|||
<Button Content="Copiar al portapapeles"
|
||||
Command="{Binding CopyToClipboardCommand}"
|
||||
Padding="10,5"
|
||||
Margin="0,0,10,0"/>
|
||||
Margin="0,0,10,0"
|
||||
IsEnabled="{Binding HasGeneratedDocumentation}"/>
|
||||
|
||||
<Button Content="Exportar..."
|
||||
Command="{Binding ExportDocumentationCommand}"
|
||||
Padding="10,5"/>
|
||||
Padding="10,5"
|
||||
IsEnabled="{Binding HasGeneratedDocumentation}"/>
|
||||
</StackPanel>
|
||||
|
||||
<xctk:PropertyGrid Grid.Row="1"
|
||||
SelectedObject="{Binding DocumentationModel}"
|
||||
AutoGenerateProperties="False">
|
||||
<xctk:PropertyGrid.PropertyDefinitions>
|
||||
<xctk:PropertyDefinition Name="Namespaces" DisplayName="Espacios de nombres"/>
|
||||
<!-- Más definiciones según la estructura del modelo -->
|
||||
</xctk:PropertyGrid.PropertyDefinitions>
|
||||
</xctk:PropertyGrid>
|
||||
<!-- Eliminamos el PropertyGrid que estaba en conflicto con el Border -->
|
||||
|
||||
<Border Grid.Row="1" Background="#f5f5f5" BorderBrush="#ddd" BorderThickness="1">
|
||||
<TextBox Text="{Binding DocumentationPreview, Mode=OneWay}"
|
||||
|
|
|
@ -1,343 +0,0 @@
|
|||
using NetDocsForLLM.Models;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace NetDocsForLLM.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementación del servicio de análisis de ensamblados basado en Reflection
|
||||
/// </summary>
|
||||
public class ReflectionAnalyzerService : IDocFxService
|
||||
{
|
||||
private readonly string _workingDirectory;
|
||||
|
||||
public ReflectionAnalyzerService()
|
||||
{
|
||||
// Crear un directorio temporal para trabajar
|
||||
_workingDirectory = Path.Combine(Path.GetTempPath(), "NetDocsForLLM_" + Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(_workingDirectory);
|
||||
}
|
||||
|
||||
public async Task<string> GenerateMetadataAsync(IEnumerable<AssemblyModel> assemblies)
|
||||
{
|
||||
// Crear directorio para metadatos
|
||||
var metadataPath = Path.Combine(_workingDirectory, "metadata");
|
||||
Directory.CreateDirectory(metadataPath);
|
||||
|
||||
// Para cada ensamblado, generar archivo de metadatos
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
await Task.Run(() => ProcessAssembly(assembly, metadataPath));
|
||||
}
|
||||
|
||||
return metadataPath;
|
||||
}
|
||||
|
||||
private void ProcessAssembly(AssemblyModel assemblyModel, string outputPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
var assembly = assemblyModel.LoadedAssembly;
|
||||
if (assembly == null)
|
||||
throw new ArgumentException("El ensamblado no está cargado", nameof(assemblyModel));
|
||||
|
||||
// Cargar comentarios XML si existen
|
||||
XDocument xmlDoc = null;
|
||||
if (assemblyModel.HasXmlDocumentation && File.Exists(assemblyModel.XmlDocPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
xmlDoc = XDocument.Load(assemblyModel.XmlDocPath);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Si hay error al cargar XML, continuar sin comentarios
|
||||
}
|
||||
}
|
||||
|
||||
// Procesar todos los tipos exportados
|
||||
foreach (var type in assembly.GetExportedTypes())
|
||||
{
|
||||
try
|
||||
{
|
||||
// Generar archivo de metadatos para cada tipo
|
||||
var typeInfo = new
|
||||
{
|
||||
Name = type.Name,
|
||||
FullName = type.FullName,
|
||||
Namespace = type.Namespace,
|
||||
IsClass = type.IsClass,
|
||||
IsInterface = type.IsInterface,
|
||||
IsEnum = type.IsEnum,
|
||||
IsAbstract = type.IsAbstract,
|
||||
IsSealed = type.IsSealed,
|
||||
IsPublic = type.IsPublic,
|
||||
BaseType = type.BaseType?.FullName,
|
||||
Interfaces = type.GetInterfaces().Select(i => i.FullName).ToArray(),
|
||||
XmlDocumentation = GetXmlDocumentation(xmlDoc, GetMemberXmlId(type)),
|
||||
Members = GetMembers(type, xmlDoc)
|
||||
};
|
||||
|
||||
// Guardar en archivo JSON
|
||||
string typePath = Path.Combine(outputPath, $"{type.FullName.Replace('+', '_')}.json");
|
||||
File.WriteAllText(typePath, JsonConvert.SerializeObject(typeInfo, Formatting.Indented));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Registrar error para este tipo pero continuar con otros
|
||||
File.WriteAllText(
|
||||
Path.Combine(outputPath, $"error_{type.FullName.Replace('+', '_')}.txt"),
|
||||
$"Error procesando tipo {type.FullName}: {ex.Message}\n{ex.StackTrace}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Registrar el error pero continuar con otros ensamblados
|
||||
File.WriteAllText(
|
||||
Path.Combine(outputPath, $"error_{Path.GetFileNameWithoutExtension(assemblyModel.FilePath)}.txt"),
|
||||
$"Error procesando ensamblado: {ex.Message}\n{ex.StackTrace}");
|
||||
}
|
||||
}
|
||||
|
||||
private object[] GetMembers(Type type, XDocument xmlDoc)
|
||||
{
|
||||
var result = new List<object>();
|
||||
|
||||
// Obtener métodos
|
||||
foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (method.IsSpecialName) // Excluir getters/setters de propiedades
|
||||
continue;
|
||||
|
||||
var parameters = method.GetParameters().Select(p => new
|
||||
{
|
||||
Name = p.Name,
|
||||
Type = GetFriendlyTypeName(p.ParameterType),
|
||||
IsOptional = p.IsOptional,
|
||||
DefaultValue = p.IsOptional ? ConvertDefaultValueToString(p.DefaultValue) : null
|
||||
}).ToArray();
|
||||
|
||||
var methodInfo = new
|
||||
{
|
||||
Name = method.Name,
|
||||
MemberType = "Method",
|
||||
ReturnType = GetFriendlyTypeName(method.ReturnType),
|
||||
IsStatic = method.IsStatic,
|
||||
IsPublic = method.IsPublic,
|
||||
Parameters = parameters,
|
||||
XmlDocumentation = GetXmlDocumentation(xmlDoc, GetMemberXmlId(method))
|
||||
};
|
||||
|
||||
result.Add(methodInfo);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignorar métodos con problemas
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Obtener propiedades
|
||||
foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly))
|
||||
{
|
||||
try
|
||||
{
|
||||
var propertyInfo = new
|
||||
{
|
||||
Name = property.Name,
|
||||
MemberType = "Property",
|
||||
Type = GetFriendlyTypeName(property.PropertyType),
|
||||
CanRead = property.CanRead,
|
||||
CanWrite = property.CanWrite,
|
||||
IsStatic = property.GetAccessors(true).FirstOrDefault()?.IsStatic ?? false,
|
||||
XmlDocumentation = GetXmlDocumentation(xmlDoc, GetMemberXmlId(property))
|
||||
};
|
||||
|
||||
result.Add(propertyInfo);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignorar propiedades con problemas
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Obtener eventos
|
||||
foreach (var eventInfo in type.GetEvents(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly))
|
||||
{
|
||||
try
|
||||
{
|
||||
var eventData = new
|
||||
{
|
||||
Name = eventInfo.Name,
|
||||
MemberType = "Event",
|
||||
EventHandlerType = GetFriendlyTypeName(eventInfo.EventHandlerType),
|
||||
IsStatic = eventInfo.GetAddMethod()?.IsStatic ?? false,
|
||||
XmlDocumentation = GetXmlDocumentation(xmlDoc, GetMemberXmlId(eventInfo))
|
||||
};
|
||||
|
||||
result.Add(eventData);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignorar eventos con problemas
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Obtener campos
|
||||
foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly))
|
||||
{
|
||||
try
|
||||
{
|
||||
var fieldInfo = new
|
||||
{
|
||||
Name = field.Name,
|
||||
MemberType = "Field",
|
||||
Type = GetFriendlyTypeName(field.FieldType),
|
||||
IsStatic = field.IsStatic,
|
||||
IsConstant = field.IsLiteral && !field.IsInitOnly,
|
||||
Value = field.IsLiteral ? ConvertDefaultValueToString(field.GetValue(null)) : null,
|
||||
XmlDocumentation = GetXmlDocumentation(xmlDoc, GetMemberXmlId(field))
|
||||
};
|
||||
|
||||
result.Add(fieldInfo);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignorar campos con problemas
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
private string GetFriendlyTypeName(Type type)
|
||||
{
|
||||
if (type == null) return "void";
|
||||
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
var genericArgs = string.Join(", ", type.GetGenericArguments().Select(GetFriendlyTypeName));
|
||||
return $"{type.Name.Split('`')[0]}<{genericArgs}>";
|
||||
}
|
||||
|
||||
// Usar nombres simplificados para tipos comunes
|
||||
if (type == typeof(void)) return "void";
|
||||
if (type == typeof(int)) return "int";
|
||||
if (type == typeof(string)) return "string";
|
||||
if (type == typeof(bool)) return "bool";
|
||||
if (type == typeof(double)) return "double";
|
||||
if (type == typeof(float)) return "float";
|
||||
if (type == typeof(decimal)) return "decimal";
|
||||
if (type == typeof(long)) return "long";
|
||||
if (type == typeof(short)) return "short";
|
||||
if (type == typeof(byte)) return "byte";
|
||||
if (type == typeof(char)) return "char";
|
||||
if (type == typeof(object)) return "object";
|
||||
|
||||
return type.FullName ?? type.Name;
|
||||
}
|
||||
|
||||
private string ConvertDefaultValueToString(object defaultValue)
|
||||
{
|
||||
if (defaultValue == null) return "null";
|
||||
|
||||
// Para strings, agregar comillas
|
||||
if (defaultValue is string stringValue)
|
||||
return $"\"{stringValue}\"";
|
||||
|
||||
return defaultValue.ToString();
|
||||
}
|
||||
|
||||
private string GetMemberXmlId(MemberInfo member)
|
||||
{
|
||||
// Generar ID XML según el formato estándar de documentación XML
|
||||
string prefix = "";
|
||||
if (member is MethodInfo)
|
||||
prefix = "M:";
|
||||
else if (member is PropertyInfo)
|
||||
prefix = "P:";
|
||||
else if (member is EventInfo)
|
||||
prefix = "E:";
|
||||
else if (member is FieldInfo)
|
||||
prefix = "F:";
|
||||
else if (member is Type)
|
||||
prefix = "T:";
|
||||
|
||||
// Manejar tipos anidados
|
||||
string declaringFullName;
|
||||
if (member.DeclaringType != null)
|
||||
{
|
||||
declaringFullName = member.DeclaringType.FullName;
|
||||
}
|
||||
else if (member is Type typeInfo)
|
||||
{
|
||||
declaringFullName = typeInfo.FullName;
|
||||
}
|
||||
else
|
||||
{
|
||||
declaringFullName = "Unknown";
|
||||
}
|
||||
|
||||
return prefix + declaringFullName + "." + member.Name;
|
||||
}
|
||||
|
||||
private object GetXmlDocumentation(XDocument xmlDoc, string memberId)
|
||||
{
|
||||
if (xmlDoc == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// Buscar nodo de miembro por ID
|
||||
var memberNode = xmlDoc.Descendants("member")
|
||||
.FirstOrDefault(m => m.Attribute("name")?.Value == memberId);
|
||||
|
||||
if (memberNode == null)
|
||||
return null;
|
||||
|
||||
// Extraer elementos de documentación
|
||||
var summary = memberNode.Element("summary")?.Value.Trim();
|
||||
var remarks = memberNode.Element("remarks")?.Value.Trim();
|
||||
var returns = memberNode.Element("returns")?.Value.Trim();
|
||||
|
||||
var parameters = memberNode.Elements("param")
|
||||
.Select(p => new
|
||||
{
|
||||
Name = p.Attribute("name")?.Value,
|
||||
Description = p.Value.Trim()
|
||||
})
|
||||
.Where(p => !string.IsNullOrEmpty(p.Name))
|
||||
.ToArray();
|
||||
|
||||
var examples = memberNode.Elements("example")
|
||||
.Select(e => e.Value.Trim())
|
||||
.ToArray();
|
||||
|
||||
return new
|
||||
{
|
||||
Summary = summary,
|
||||
Remarks = remarks,
|
||||
Returns = returns,
|
||||
Parameters = parameters,
|
||||
Examples = examples
|
||||
};
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,16 +5,12 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using Formatting = Newtonsoft.Json.Formatting;
|
||||
|
||||
namespace NetDocsForLLM.Services
|
||||
{
|
||||
public interface IDocumentationGenerator
|
||||
{
|
||||
Task<DocumentationModel> GenerateDocumentation(IEnumerable<AssemblyModel> assemblies, ExportSettings settings);
|
||||
string GenerateDocumentationPreview(DocumentationModel documentation, ExportSettings settings);
|
||||
}
|
||||
|
||||
public class DocumentationGenerator : IDocumentationGenerator
|
||||
{
|
||||
private readonly IDocFxService _metadataService;
|
||||
|
@ -28,21 +24,18 @@ namespace NetDocsForLLM.Services
|
|||
{
|
||||
try
|
||||
{
|
||||
// Generate metadata using reflection service
|
||||
// Generate metadata using XmlDocGenerator
|
||||
var metadataPath = await _metadataService.GenerateMetadataAsync(assemblies);
|
||||
|
||||
// Process metadata files
|
||||
// Create a new documentation model
|
||||
var documentation = new DocumentationModel();
|
||||
var namespaces = new Dictionary<string, NamespaceDocumentation>();
|
||||
|
||||
// Read JSON files produced by our reflection service
|
||||
var jsonFiles = Directory.GetFiles(metadataPath, "*.json", SearchOption.AllDirectories);
|
||||
foreach (var jsonFile in jsonFiles)
|
||||
// Process XML files produced by our XmlDocGenerator
|
||||
var xmlFiles = Directory.GetFiles(metadataPath, "*.xml");
|
||||
foreach (var xmlFile in xmlFiles)
|
||||
{
|
||||
if (Path.GetFileName(jsonFile).StartsWith("error_"))
|
||||
continue; // Skip error files
|
||||
|
||||
ProcessJsonMetadata(jsonFile, documentation, namespaces, settings);
|
||||
ProcessXmlDocumentation(xmlFile, documentation, namespaces, settings);
|
||||
}
|
||||
|
||||
return documentation;
|
||||
|
@ -53,211 +46,334 @@ namespace NetDocsForLLM.Services
|
|||
}
|
||||
}
|
||||
|
||||
private void ProcessJsonMetadata(string jsonFile, DocumentationModel documentation,
|
||||
private void ProcessXmlDocumentation(string xmlFile, DocumentationModel documentation,
|
||||
Dictionary<string, NamespaceDocumentation> namespaces,
|
||||
ExportSettings settings)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Leer el archivo JSON
|
||||
var jsonContent = File.ReadAllText(jsonFile);
|
||||
var typeData = JsonConvert.DeserializeObject<dynamic>(jsonContent);
|
||||
// Cargar el archivo XML
|
||||
var xmlDoc = XDocument.Load(xmlFile);
|
||||
|
||||
if (typeData == null)
|
||||
// Obtener el nombre del ensamblado
|
||||
string assemblyName = xmlDoc.Root.Element("assembly")?.Element("name")?.Value;
|
||||
if (string.IsNullOrEmpty(assemblyName))
|
||||
return;
|
||||
|
||||
// Obtener namespace
|
||||
string namespaceName = typeData.Namespace?.ToString() ?? "Global";
|
||||
// Leer todos los miembros
|
||||
var memberElements = xmlDoc.Root.Element("members")?.Elements("member");
|
||||
if (memberElements == null)
|
||||
return;
|
||||
|
||||
// Buscar o crear el namespace en nuestra documentación
|
||||
// Procesar los tipos primero (para la estructura jerárquica)
|
||||
var typeMembers = memberElements.Where(m =>
|
||||
m.Attribute("name")?.Value?.StartsWith("T:") == true);
|
||||
|
||||
foreach (var typeMember in typeMembers)
|
||||
{
|
||||
ProcessTypeMember(typeMember, documentation, namespaces);
|
||||
}
|
||||
|
||||
// Luego procesar miembros de tipos
|
||||
var nonTypeMembers = memberElements.Where(m =>
|
||||
m.Attribute("name")?.Value?.StartsWith("T:") == false);
|
||||
|
||||
foreach (var member in nonTypeMembers)
|
||||
{
|
||||
ProcessMember(member, documentation, namespaces, settings);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error procesando archivo XML {xmlFile}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessTypeMember(XElement typeMember, DocumentationModel documentation,
|
||||
Dictionary<string, NamespaceDocumentation> namespaces)
|
||||
{
|
||||
try
|
||||
{
|
||||
string typeId = typeMember.Attribute("name")?.Value;
|
||||
if (string.IsNullOrEmpty(typeId) || !typeId.StartsWith("T:"))
|
||||
return;
|
||||
|
||||
// Extraer el nombre completo del tipo
|
||||
string fullTypeName = typeId.Substring(2); // Quitar "T:"
|
||||
|
||||
// Obtener namespace y nombre del tipo
|
||||
string namespaceName = "Global";
|
||||
string typeName = fullTypeName;
|
||||
|
||||
int lastDotPos = fullTypeName.LastIndexOf('.');
|
||||
if (lastDotPos > 0)
|
||||
{
|
||||
namespaceName = fullTypeName.Substring(0, lastDotPos);
|
||||
typeName = fullTypeName.Substring(lastDotPos + 1);
|
||||
}
|
||||
|
||||
// Buscar o crear el namespace
|
||||
if (!namespaces.TryGetValue(namespaceName, out var namespaceDoc))
|
||||
{
|
||||
namespaceDoc = new NamespaceDocumentation
|
||||
{
|
||||
Name = namespaceName,
|
||||
Description = $"Contiene tipos del ensamblado"
|
||||
Description = $"Contains types from the assembly"
|
||||
};
|
||||
namespaces[namespaceName] = namespaceDoc;
|
||||
documentation.Namespaces.Add(namespaceDoc);
|
||||
}
|
||||
|
||||
// Obtener información del tipo desde XML
|
||||
string description = typeMember.Element("summary")?.Value?.Trim() ?? "No description available";
|
||||
string remarks = typeMember.Element("remarks")?.Value?.Trim();
|
||||
|
||||
// Determinar el tipo de tipo
|
||||
string typeKind = "Class";
|
||||
if (description.Contains("interface:"))
|
||||
typeKind = "Interface";
|
||||
else if (description.Contains("structure:"))
|
||||
typeKind = "Struct";
|
||||
else if (description.Contains("enumeration:"))
|
||||
typeKind = "Enum";
|
||||
else if (description.Contains("abstract class:"))
|
||||
typeKind = "Abstract Class";
|
||||
else if (description.Contains("static class:"))
|
||||
typeKind = "Static Class";
|
||||
|
||||
// Crear documentación de tipo
|
||||
var typeDoc = new TypeDocumentation
|
||||
{
|
||||
Name = typeData.Name,
|
||||
FullName = typeData.FullName,
|
||||
Description = typeData.XmlDocumentation?.Summary ?? "Sin documentación disponible",
|
||||
TypeKind = GetTypeKind(typeData)
|
||||
Name = typeName,
|
||||
FullName = fullTypeName,
|
||||
Description = description,
|
||||
TypeKind = typeKind
|
||||
};
|
||||
|
||||
// Agregar base types e interfaces si están disponibles
|
||||
if (typeData.BaseType != null && typeData.BaseType.ToString() != "System.Object")
|
||||
// Procesar herencia e interfaces si están disponibles en remarks
|
||||
if (!string.IsNullOrEmpty(remarks))
|
||||
{
|
||||
typeDoc.BaseTypes.Add(typeData.BaseType.ToString());
|
||||
if (remarks.Contains("Inherits from"))
|
||||
{
|
||||
int start = remarks.IndexOf("Inherits from") + "Inherits from".Length;
|
||||
int end = remarks.IndexOf("\n", start);
|
||||
if (end < 0) end = remarks.Length;
|
||||
|
||||
string baseTypeName = remarks.Substring(start, end - start).Trim();
|
||||
typeDoc.BaseTypes.Add(baseTypeName);
|
||||
}
|
||||
|
||||
// Agregar interfaces
|
||||
if (typeData.Interfaces != null)
|
||||
if (remarks.Contains("Implements:"))
|
||||
{
|
||||
foreach (var interfaceType in typeData.Interfaces)
|
||||
{
|
||||
typeDoc.Interfaces.Add(interfaceType.ToString());
|
||||
}
|
||||
}
|
||||
int start = remarks.IndexOf("Implements:") + "Implements:".Length;
|
||||
int end = remarks.IndexOf("\n", start);
|
||||
if (end < 0) end = remarks.Length;
|
||||
|
||||
// Procesar miembros
|
||||
if (typeData.Members != null)
|
||||
{
|
||||
foreach (var member in typeData.Members)
|
||||
{
|
||||
// Filtrar miembros privados si la configuración lo indica
|
||||
if (!settings.IncludePrivateMembers &&
|
||||
member.IsPublic != null && !(bool)member.IsPublic)
|
||||
continue;
|
||||
string interfacesStr = remarks.Substring(start, end - start).Trim();
|
||||
string[] interfaces = interfacesStr.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
var memberDoc = new MemberDocumentation
|
||||
foreach (var interfaceName in interfaces)
|
||||
{
|
||||
Name = member.Name,
|
||||
Description = member.XmlDocumentation?.Summary ?? "Sin documentación disponible",
|
||||
MemberType = member.MemberType,
|
||||
Signature = member.Signature ?? GenerateSignature(member),
|
||||
ReturnType = GetReturnType(member),
|
||||
ReturnDescription = member.XmlDocumentation?.Returns ?? ""
|
||||
};
|
||||
|
||||
// Agregar parámetros si es un método
|
||||
if (member.MemberType?.ToString() == "Method" && member.Parameters != null)
|
||||
{
|
||||
foreach (var param in member.Parameters)
|
||||
{
|
||||
var paramDoc = new ParameterDocumentation
|
||||
{
|
||||
Name = param.Name,
|
||||
Type = param.Type,
|
||||
IsOptional = param.IsOptional != null && (bool)param.IsOptional,
|
||||
DefaultValue = param.DefaultValue?.ToString() ?? "",
|
||||
Description = GetParameterDescription(member.XmlDocumentation, param.Name?.ToString())
|
||||
};
|
||||
|
||||
memberDoc.Parameters.Add(paramDoc);
|
||||
typeDoc.Interfaces.Add(interfaceName.Trim());
|
||||
}
|
||||
}
|
||||
|
||||
// Agregar ejemplos si están disponibles y configurados
|
||||
if (settings.IncludeExamples &&
|
||||
member.XmlDocumentation?.Examples != null)
|
||||
{
|
||||
foreach (var example in member.XmlDocumentation.Examples)
|
||||
{
|
||||
if (example != null && !string.IsNullOrWhiteSpace(example.ToString()))
|
||||
memberDoc.Examples.Add(example.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
typeDoc.Members.Add(memberDoc);
|
||||
}
|
||||
}
|
||||
|
||||
namespaceDoc.Types.Add(typeDoc);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Si hay error al procesar, lo registramos pero continuamos
|
||||
Console.WriteLine($"Error al procesar {jsonFile}: {ex.Message}");
|
||||
Console.WriteLine($"Error procesando tipo: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private string GetTypeKind(dynamic typeData)
|
||||
private void ProcessMember(XElement memberElement, DocumentationModel documentation,
|
||||
Dictionary<string, NamespaceDocumentation> namespaces,
|
||||
ExportSettings settings)
|
||||
{
|
||||
if (typeData.IsClass != null && (bool)typeData.IsClass)
|
||||
return "Class";
|
||||
if (typeData.IsInterface != null && (bool)typeData.IsInterface)
|
||||
return "Interface";
|
||||
if (typeData.IsEnum != null && (bool)typeData.IsEnum)
|
||||
return "Enum";
|
||||
return "Type";
|
||||
try
|
||||
{
|
||||
string memberId = memberElement.Attribute("name")?.Value;
|
||||
if (string.IsNullOrEmpty(memberId))
|
||||
return;
|
||||
|
||||
// Determinar el tipo de miembro
|
||||
char memberTypeChar = memberId[0];
|
||||
if (memberId.Length < 3 || memberId[1] != ':')
|
||||
return;
|
||||
|
||||
string memberType;
|
||||
switch (memberTypeChar)
|
||||
{
|
||||
case 'M': memberType = "Method"; break;
|
||||
case 'P': memberType = "Property"; break;
|
||||
case 'F': memberType = "Field"; break;
|
||||
case 'E': memberType = "Event"; break;
|
||||
default: return; // No es un miembro que procesamos
|
||||
}
|
||||
|
||||
private string GenerateSignature(dynamic member)
|
||||
{
|
||||
// Generar una firma simple basada en el tipo de miembro
|
||||
string memberType = member.MemberType?.ToString();
|
||||
string name = member.Name?.ToString() ?? "Unknown";
|
||||
// Extraer información del tipo al que pertenece el miembro
|
||||
string fullMemberId = memberId.Substring(2); // Quitar "X:"
|
||||
int lastDotPos = fullMemberId.LastIndexOf('.');
|
||||
if (lastDotPos <= 0)
|
||||
return;
|
||||
|
||||
string fullTypeName = fullMemberId.Substring(0, lastDotPos);
|
||||
string memberName = fullMemberId.Substring(lastDotPos + 1);
|
||||
|
||||
// Para métodos con parámetros, extraer el nombre real
|
||||
if (memberType == "Method" && memberName.Contains("("))
|
||||
{
|
||||
memberName = memberName.Substring(0, memberName.IndexOf('('));
|
||||
}
|
||||
|
||||
// Encontrar el namespace y tipo
|
||||
string namespaceName = "Global";
|
||||
string typeName = fullTypeName;
|
||||
|
||||
int lastTypeNameDotPos = fullTypeName.LastIndexOf('.');
|
||||
if (lastTypeNameDotPos > 0)
|
||||
{
|
||||
namespaceName = fullTypeName.Substring(0, lastTypeNameDotPos);
|
||||
typeName = fullTypeName.Substring(lastTypeNameDotPos + 1);
|
||||
}
|
||||
|
||||
// Buscar el namespace
|
||||
if (!namespaces.TryGetValue(namespaceName, out var namespaceDoc))
|
||||
{
|
||||
return; // No encontramos el namespace, lo cual es extraño
|
||||
}
|
||||
|
||||
// Buscar el tipo
|
||||
var typeDoc = namespaceDoc.Types.FirstOrDefault(t => t.FullName == fullTypeName || t.Name == typeName);
|
||||
if (typeDoc == null)
|
||||
{
|
||||
return; // No encontramos el tipo, lo cual es extraño
|
||||
}
|
||||
|
||||
// Extraer descripción y otra información
|
||||
string description = memberElement.Element("summary")?.Value?.Trim() ?? "No description available";
|
||||
string returns = memberElement.Element("returns")?.Value?.Trim();
|
||||
string value = memberElement.Element("value")?.Value?.Trim();
|
||||
|
||||
// Crear documentación del miembro
|
||||
var memberDoc = new MemberDocumentation
|
||||
{
|
||||
Name = memberName,
|
||||
MemberType = memberType,
|
||||
Description = description,
|
||||
ReturnType = DeriveReturnType(memberType, returns, value)
|
||||
};
|
||||
|
||||
// Para métodos, procesar parámetros
|
||||
if (memberType == "Method")
|
||||
{
|
||||
string returnType = member.ReturnType?.ToString() ?? "void";
|
||||
string parameters = "";
|
||||
foreach (var paramElement in memberElement.Elements("param"))
|
||||
{
|
||||
string paramName = paramElement.Attribute("name")?.Value;
|
||||
string paramDesc = paramElement.Value?.Trim();
|
||||
|
||||
if (member.Parameters != null)
|
||||
if (!string.IsNullOrEmpty(paramName))
|
||||
{
|
||||
var paramList = new List<string>();
|
||||
foreach (var param in member.Parameters)
|
||||
// Extraer tipo del parámetro de la descripción
|
||||
string paramType = "object";
|
||||
if (!string.IsNullOrEmpty(paramDesc) && paramDesc.StartsWith("A "))
|
||||
{
|
||||
string paramType = param.Type?.ToString() ?? "object";
|
||||
string paramName = param.Name?.ToString() ?? "param";
|
||||
paramList.Add($"{paramType} {paramName}");
|
||||
int spacePos = paramDesc.IndexOf(' ', 2);
|
||||
if (spacePos > 0)
|
||||
{
|
||||
paramType = paramDesc.Substring(2, spacePos - 2);
|
||||
}
|
||||
parameters = string.Join(", ", paramList);
|
||||
}
|
||||
|
||||
return $"{returnType} {name}({parameters})";
|
||||
var paramDoc = new ParameterDocumentation
|
||||
{
|
||||
Name = paramName,
|
||||
Type = paramType,
|
||||
Description = paramDesc,
|
||||
IsOptional = paramDesc?.Contains("Optional") == true
|
||||
};
|
||||
|
||||
memberDoc.Parameters.Add(paramDoc);
|
||||
}
|
||||
}
|
||||
|
||||
// Generar firma basada en parámetros
|
||||
string returnType = memberDoc.ReturnType ?? "void";
|
||||
string parameters = string.Join(", ", memberDoc.Parameters.Select(p => $"{p.Type} {p.Name}"));
|
||||
memberDoc.Signature = $"{returnType} {memberName}({parameters})";
|
||||
}
|
||||
else if (memberType == "Property")
|
||||
{
|
||||
string propType = member.Type?.ToString() ?? "object";
|
||||
// Generar firma para propiedades
|
||||
string propType = memberDoc.ReturnType ?? "object";
|
||||
string accessors = "";
|
||||
|
||||
bool canRead = member.CanRead != null && (bool)member.CanRead;
|
||||
bool canWrite = member.CanWrite != null && (bool)member.CanWrite;
|
||||
|
||||
if (canRead && canWrite)
|
||||
accessors = " { get; set; }";
|
||||
else if (canRead)
|
||||
accessors = " { get; }";
|
||||
else if (canWrite)
|
||||
accessors = " { set; }";
|
||||
|
||||
return $"{propType} {name}{accessors}";
|
||||
}
|
||||
else if (memberType == "Field")
|
||||
if (description.Contains("Gets"))
|
||||
{
|
||||
string fieldType = member.Type?.ToString() ?? "object";
|
||||
return $"{fieldType} {name}";
|
||||
accessors += "get; ";
|
||||
}
|
||||
else if (memberType == "Event")
|
||||
|
||||
if (description.Contains("sets"))
|
||||
{
|
||||
string eventType = member.EventHandlerType?.ToString() ?? "EventHandler";
|
||||
return $"event {eventType} {name}";
|
||||
accessors += "set; ";
|
||||
}
|
||||
|
||||
return name;
|
||||
memberDoc.Signature = $"{propType} {memberName} {{ {accessors}}}";
|
||||
}
|
||||
|
||||
private string GetReturnType(dynamic member)
|
||||
else
|
||||
{
|
||||
string memberType = member.MemberType?.ToString();
|
||||
|
||||
if (memberType == "Method")
|
||||
return member.ReturnType?.ToString() ?? "void";
|
||||
else if (memberType == "Property")
|
||||
return member.Type?.ToString() ?? "object";
|
||||
|
||||
return "";
|
||||
// Firma simple para otros miembros
|
||||
memberDoc.Signature = $"{memberDoc.ReturnType ?? "void"} {memberName}";
|
||||
}
|
||||
|
||||
private string GetParameterDescription(dynamic xmlDocumentation, string paramName)
|
||||
// Agregar el miembro al tipo
|
||||
typeDoc.Members.Add(memberDoc);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (xmlDocumentation?.Parameters == null || paramName == null)
|
||||
return "";
|
||||
|
||||
foreach (var param in xmlDocumentation.Parameters)
|
||||
{
|
||||
if (param.Name?.ToString() == paramName)
|
||||
return param.Description?.ToString() ?? "";
|
||||
Console.WriteLine($"Error procesando miembro: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
private string DeriveReturnType(string memberType, string returns, string value)
|
||||
{
|
||||
// Intentar extraer el tipo de retorno de las etiquetas returns o value
|
||||
if (!string.IsNullOrEmpty(returns))
|
||||
{
|
||||
// Intentar extraer de algo como "A string value."
|
||||
if (returns.StartsWith("A ") || returns.StartsWith("An "))
|
||||
{
|
||||
int spacePos = returns.IndexOf(' ', 2);
|
||||
if (spacePos > 0)
|
||||
{
|
||||
return returns.Substring(2, spacePos - 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(value))
|
||||
{
|
||||
// Intentar extraer de algo como "A string representing..."
|
||||
if (value.StartsWith("A ") || value.StartsWith("An "))
|
||||
{
|
||||
int spacePos = value.IndexOf(' ', 2);
|
||||
if (spacePos > 0)
|
||||
{
|
||||
return value.Substring(2, spacePos - 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Valores por defecto para diferentes tipos de miembros
|
||||
switch (memberType)
|
||||
{
|
||||
case "Method": return "void";
|
||||
case "Property": return "object";
|
||||
case "Field": return "object";
|
||||
case "Event": return "EventHandler";
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
public string GenerateDocumentationPreview(DocumentationModel documentation, ExportSettings settings)
|
||||
|
@ -276,14 +392,140 @@ namespace NetDocsForLLM.Services
|
|||
else // YAML
|
||||
{
|
||||
// En un escenario real, usaríamos YamlDotNet
|
||||
// Para esta implementación, usaremos una conversión manual simplificada
|
||||
return ConvertJsonToSimpleYaml(
|
||||
JsonConvert.SerializeObject(documentation, Formatting.None,
|
||||
new JsonSerializerSettings
|
||||
// Para esta implementación, usaremos una conversión manual simplificada a XML
|
||||
var doc = new XDocument(
|
||||
new XDeclaration("1.0", "utf-8", null),
|
||||
new XElement("documentation",
|
||||
new XElement("namespaces")
|
||||
)
|
||||
);
|
||||
|
||||
var namespacesElement = doc.Root.Element("namespaces");
|
||||
|
||||
foreach (var ns in documentation.Namespaces)
|
||||
{
|
||||
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
|
||||
NullValueHandling = NullValueHandling.Ignore
|
||||
}));
|
||||
var nsElement = new XElement("namespace",
|
||||
new XAttribute("name", ns.Name),
|
||||
new XElement("description", ns.Description),
|
||||
new XElement("types")
|
||||
);
|
||||
|
||||
var typesElement = nsElement.Element("types");
|
||||
|
||||
foreach (var type in ns.Types)
|
||||
{
|
||||
var typeElement = new XElement("type",
|
||||
new XAttribute("name", type.Name),
|
||||
new XAttribute("fullName", type.FullName),
|
||||
new XAttribute("kind", type.TypeKind),
|
||||
new XElement("description", type.Description),
|
||||
new XElement("members")
|
||||
);
|
||||
|
||||
if (type.BaseTypes.Count > 0)
|
||||
{
|
||||
var baseTypesElement = new XElement("baseTypes");
|
||||
foreach (var baseType in type.BaseTypes)
|
||||
{
|
||||
baseTypesElement.Add(new XElement("baseType", baseType));
|
||||
}
|
||||
typeElement.Add(baseTypesElement);
|
||||
}
|
||||
|
||||
if (type.Interfaces.Count > 0)
|
||||
{
|
||||
var interfacesElement = new XElement("interfaces");
|
||||
foreach (var iface in type.Interfaces)
|
||||
{
|
||||
interfacesElement.Add(new XElement("interface", iface));
|
||||
}
|
||||
typeElement.Add(interfacesElement);
|
||||
}
|
||||
|
||||
var membersElement = typeElement.Element("members");
|
||||
|
||||
foreach (var member in type.Members)
|
||||
{
|
||||
var memberElement = new XElement("member",
|
||||
new XAttribute("name", member.Name),
|
||||
new XAttribute("type", member.MemberType),
|
||||
new XElement("description", member.Description),
|
||||
new XElement("signature", member.Signature)
|
||||
);
|
||||
|
||||
if (!string.IsNullOrEmpty(member.ReturnType))
|
||||
{
|
||||
memberElement.Add(new XElement("returnType", member.ReturnType));
|
||||
|
||||
if (!string.IsNullOrEmpty(member.ReturnDescription))
|
||||
{
|
||||
memberElement.Add(new XElement("returnDescription", member.ReturnDescription));
|
||||
}
|
||||
}
|
||||
|
||||
if (member.Parameters.Count > 0)
|
||||
{
|
||||
var paramsElement = new XElement("parameters");
|
||||
|
||||
foreach (var param in member.Parameters)
|
||||
{
|
||||
var paramElement = new XElement("parameter",
|
||||
new XAttribute("name", param.Name),
|
||||
new XAttribute("type", param.Type),
|
||||
new XElement("description", param.Description ?? "")
|
||||
);
|
||||
|
||||
if (param.IsOptional)
|
||||
{
|
||||
paramElement.Add(new XAttribute("optional", "true"));
|
||||
|
||||
if (!string.IsNullOrEmpty(param.DefaultValue))
|
||||
{
|
||||
paramElement.Add(new XAttribute("defaultValue", param.DefaultValue));
|
||||
}
|
||||
}
|
||||
|
||||
paramsElement.Add(paramElement);
|
||||
}
|
||||
|
||||
memberElement.Add(paramsElement);
|
||||
}
|
||||
|
||||
if (member.Examples.Count > 0)
|
||||
{
|
||||
var examplesElement = new XElement("examples");
|
||||
|
||||
foreach (var example in member.Examples)
|
||||
{
|
||||
examplesElement.Add(new XElement("example", example));
|
||||
}
|
||||
|
||||
memberElement.Add(examplesElement);
|
||||
}
|
||||
|
||||
membersElement.Add(memberElement);
|
||||
}
|
||||
|
||||
typesElement.Add(typeElement);
|
||||
}
|
||||
|
||||
namespacesElement.Add(nsElement);
|
||||
}
|
||||
|
||||
// Convertir a string con formato
|
||||
using (var stringWriter = new StringWriter())
|
||||
{
|
||||
using (var xmlWriter = new XmlTextWriter(stringWriter)
|
||||
{
|
||||
Formatting = (System.Xml.Formatting)Formatting.Indented,
|
||||
Indentation = 2
|
||||
})
|
||||
{
|
||||
doc.Save(xmlWriter);
|
||||
}
|
||||
|
||||
return stringWriter.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -291,84 +533,5 @@ namespace NetDocsForLLM.Services
|
|||
throw new InvalidOperationException($"Error al generar vista previa: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private string ConvertJsonToSimpleYaml(string json)
|
||||
{
|
||||
// Deserialize JSON
|
||||
var obj = JsonConvert.DeserializeObject<dynamic>(json);
|
||||
|
||||
// Basic YAML builder
|
||||
var yaml = new System.Text.StringBuilder();
|
||||
yaml.AppendLine("namespaces:");
|
||||
|
||||
foreach (var ns in obj.Namespaces)
|
||||
{
|
||||
yaml.AppendLine($" - name: {ns.Name}");
|
||||
yaml.AppendLine($" description: {ns.Description}");
|
||||
yaml.AppendLine(" types:");
|
||||
|
||||
foreach (var type in ns.Types)
|
||||
{
|
||||
yaml.AppendLine($" - name: {type.Name}");
|
||||
yaml.AppendLine($" fullName: {type.FullName}");
|
||||
yaml.AppendLine($" typeKind: {type.TypeKind}");
|
||||
yaml.AppendLine($" description: {type.Description}");
|
||||
yaml.AppendLine(" members:");
|
||||
|
||||
foreach (var member in type.Members)
|
||||
{
|
||||
yaml.AppendLine($" - name: {member.Name}");
|
||||
yaml.AppendLine($" memberType: {member.MemberType}");
|
||||
yaml.AppendLine($" signature: \"{EscapeYamlString(member.Signature)}\"");
|
||||
yaml.AppendLine($" description: \"{EscapeYamlString(member.Description)}\"");
|
||||
|
||||
if (member.Parameters != null && member.Parameters.Count > 0)
|
||||
{
|
||||
yaml.AppendLine(" parameters:");
|
||||
foreach (var param in member.Parameters)
|
||||
{
|
||||
yaml.AppendLine($" - name: {param.Name}");
|
||||
yaml.AppendLine($" type: {param.Type}");
|
||||
yaml.AppendLine($" description: \"{EscapeYamlString(param.Description)}\"");
|
||||
yaml.AppendLine($" isOptional: {param.IsOptional.ToString().ToLower()}");
|
||||
if (!string.IsNullOrEmpty(param.DefaultValue))
|
||||
yaml.AppendLine($" defaultValue: \"{EscapeYamlString(param.DefaultValue)}\"");
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(member.ReturnType))
|
||||
{
|
||||
yaml.AppendLine($" returnType: {member.ReturnType}");
|
||||
if (!string.IsNullOrEmpty(member.ReturnDescription))
|
||||
yaml.AppendLine($" returnDescription: \"{EscapeYamlString(member.ReturnDescription)}\"");
|
||||
}
|
||||
|
||||
if (member.Examples != null && member.Examples.Count > 0)
|
||||
{
|
||||
yaml.AppendLine(" examples:");
|
||||
foreach (var example in member.Examples)
|
||||
{
|
||||
yaml.AppendLine($" - |\n {example.Replace("\n", "\n ")}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return yaml.ToString();
|
||||
}
|
||||
|
||||
private string EscapeYamlString(string input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
return "";
|
||||
|
||||
return input
|
||||
.Replace("\\", "\\\\")
|
||||
.Replace("\"", "\\\"")
|
||||
.Replace("\n", "\\n")
|
||||
.Replace("\r", "\\r")
|
||||
.Replace("\t", "\\t");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
using NetDocsForLLM.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NetDocsForLLM.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Interfaz para servicios de generación de documentación a partir de ensamblados
|
||||
/// </summary>
|
||||
public interface IDocumentationGenerator
|
||||
{
|
||||
/// <summary>
|
||||
/// Genera un modelo de documentación a partir de los ensamblados proporcionados
|
||||
/// </summary>
|
||||
/// <param name="assemblies">Ensamblados para documentar</param>
|
||||
/// <param name="settings">Configuración de exportación</param>
|
||||
/// <returns>Modelo de documentación estructurado</returns>
|
||||
Task<DocumentationModel> GenerateDocumentation(IEnumerable<AssemblyModel> assemblies, ExportSettings settings);
|
||||
|
||||
/// <summary>
|
||||
/// Genera una vista previa de la documentación en formato texto (JSON o YAML)
|
||||
/// </summary>
|
||||
/// <param name="documentation">Modelo de documentación</param>
|
||||
/// <param name="settings">Configuración de exportación</param>
|
||||
/// <returns>Representación textual de la documentación</returns>
|
||||
string GenerateDocumentationPreview(DocumentationModel documentation, ExportSettings settings);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,504 @@
|
|||
using NetDocsForLLM.Models;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace NetDocsForLLM.Services
|
||||
{
|
||||
public class ReflectionAnalyzerService : IDocFxService
|
||||
{
|
||||
private readonly string _workingDirectory;
|
||||
private TextWriter _logWriter;
|
||||
private XDocument _xmlDoc; // Variable de clase para acceder al XML desde cualquier método
|
||||
|
||||
public ReflectionAnalyzerService()
|
||||
{
|
||||
// Crear un directorio temporal para trabajar
|
||||
_workingDirectory = Path.Combine(Path.GetTempPath(), "NetDocsForLLM_" + Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(_workingDirectory);
|
||||
}
|
||||
|
||||
public async Task<string> GenerateMetadataAsync(IEnumerable<AssemblyModel> assemblies)
|
||||
{
|
||||
// Crear directorio para metadatos
|
||||
var metadataPath = Path.Combine(_workingDirectory, "metadata");
|
||||
Directory.CreateDirectory(metadataPath);
|
||||
|
||||
// Crear archivo de log
|
||||
var logPath = Path.Combine(metadataPath, "analysis_log.txt");
|
||||
using (_logWriter = new StreamWriter(logPath, false))
|
||||
{
|
||||
_logWriter.WriteLine($"Iniciando análisis simplificado para LLMs: {DateTime.Now}");
|
||||
|
||||
// Para cada ensamblado, generar archivo de metadatos
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
await Task.Run(() => ProcessAssembly(assembly, metadataPath));
|
||||
}
|
||||
}
|
||||
|
||||
return metadataPath;
|
||||
}
|
||||
|
||||
private void ProcessAssembly(AssemblyModel assemblyModel, string outputPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logWriter.WriteLine($"\n=== Procesando ensamblado: {assemblyModel.Name} ===");
|
||||
|
||||
Assembly assembly = assemblyModel.LoadedAssembly;
|
||||
if (assembly == null)
|
||||
{
|
||||
_logWriter.WriteLine("ERROR: El ensamblado no está cargado");
|
||||
return;
|
||||
}
|
||||
|
||||
// Cargar documentación XML si existe
|
||||
_xmlDoc = null;
|
||||
if (assemblyModel.HasXmlDocumentation && File.Exists(assemblyModel.XmlDocPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
_xmlDoc = XDocument.Load(assemblyModel.XmlDocPath);
|
||||
_logWriter.WriteLine($"XML cargado: {assemblyModel.XmlDocPath}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logWriter.WriteLine($"Error cargando XML: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// Procesar todos los tipos exportados
|
||||
var exportedTypes = assembly.GetExportedTypes()
|
||||
.Where(t => t.IsPublic && !t.IsSpecialName) // Solo tipos públicos y no especiales
|
||||
.ToList();
|
||||
|
||||
_logWriter.WriteLine($"Tipos exportados encontrados: {exportedTypes.Count}");
|
||||
|
||||
// Agrupar tipos por namespace
|
||||
var namespaces = new List<object>();
|
||||
foreach (var nsGroup in exportedTypes.GroupBy(t => t.Namespace ?? "Global"))
|
||||
{
|
||||
string namespaceName = nsGroup.Key;
|
||||
var types = new List<object>();
|
||||
|
||||
_logWriter.WriteLine($"\nProcesando namespace: {namespaceName}");
|
||||
|
||||
foreach (var type in nsGroup)
|
||||
{
|
||||
// Solo incluir tipos que no sean anidados o que sean útiles
|
||||
if (!type.IsNested || type.IsNestedPublic)
|
||||
{
|
||||
types.Add(ExtractSimplifiedTypeInfo(type));
|
||||
}
|
||||
}
|
||||
|
||||
if (types.Count > 0)
|
||||
{
|
||||
namespaces.Add(new
|
||||
{
|
||||
Name = namespaceName,
|
||||
Types = types.ToArray()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Guardar toda la documentación en un solo archivo
|
||||
var docStructure = new
|
||||
{
|
||||
AssemblyName = assembly.GetName().Name,
|
||||
Version = assembly.GetName().Version.ToString(),
|
||||
Namespaces = namespaces.ToArray()
|
||||
};
|
||||
|
||||
string outputFile = Path.Combine(outputPath, $"assembly_{assemblyModel.Name}.json");
|
||||
File.WriteAllText(outputFile, JsonConvert.SerializeObject(docStructure, Formatting.Indented));
|
||||
|
||||
_logWriter.WriteLine($"Documentación guardada en: {outputFile}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logWriter.WriteLine($"ERROR procesando ensamblado: {ex.Message}");
|
||||
_logWriter.WriteLine(ex.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
private object ExtractSimplifiedTypeInfo(Type type)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logWriter.WriteLine($" Documentando tipo: {type.Name}");
|
||||
|
||||
// Obtener comentario XML del tipo
|
||||
string description = GetXmlSummary(type);
|
||||
|
||||
// Obtener miembros
|
||||
var members = new List<object>();
|
||||
|
||||
// Métodos públicos (no especiales como getters/setters)
|
||||
foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)
|
||||
.Where(m => !m.IsSpecialName))
|
||||
{
|
||||
var memberObject = DocumentMethod(method);
|
||||
if (memberObject != null)
|
||||
members.Add(memberObject);
|
||||
}
|
||||
|
||||
// Propiedades públicas
|
||||
foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly))
|
||||
{
|
||||
var memberObject = DocumentProperty(property);
|
||||
if (memberObject != null)
|
||||
members.Add(memberObject);
|
||||
}
|
||||
|
||||
// Campos públicos
|
||||
foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly))
|
||||
{
|
||||
var memberObject = DocumentField(field);
|
||||
if (memberObject != null)
|
||||
members.Add(memberObject);
|
||||
}
|
||||
|
||||
// Eventos públicos
|
||||
foreach (var eventInfo in type.GetEvents(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly))
|
||||
{
|
||||
var memberObject = DocumentEvent(eventInfo);
|
||||
if (memberObject != null)
|
||||
members.Add(memberObject);
|
||||
}
|
||||
|
||||
_logWriter.WriteLine($" Miembros documentados: {members.Count}");
|
||||
|
||||
// Crear objeto de información de tipo
|
||||
var typeInfo = new
|
||||
{
|
||||
Name = type.Name,
|
||||
FullName = type.FullName,
|
||||
Kind = GetTypeKind(type),
|
||||
Description = description,
|
||||
// Solo incluir interfaces si hay alguna
|
||||
Interfaces = type.GetInterfaces().Length > 0
|
||||
? type.GetInterfaces().Select(i => i.Name).ToArray()
|
||||
: null,
|
||||
// Solo incluir base si no es Object
|
||||
BaseType = (type.BaseType != null && type.BaseType != typeof(object))
|
||||
? type.BaseType.Name
|
||||
: null,
|
||||
// Solo incluir miembros si hay alguno
|
||||
Members = members.Count > 0 ? members.ToArray() : null
|
||||
};
|
||||
|
||||
return typeInfo;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logWriter.WriteLine($" ERROR documentando tipo {type.Name}: {ex.Message}");
|
||||
|
||||
// Devolver información mínima
|
||||
return new
|
||||
{
|
||||
Name = type.Name,
|
||||
FullName = type.FullName,
|
||||
Kind = GetTypeKind(type),
|
||||
Error = "Error al extraer información"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private object DocumentMethod(MethodInfo method)
|
||||
{
|
||||
try
|
||||
{
|
||||
string description = GetXmlSummary(method);
|
||||
|
||||
// Parametros
|
||||
var parameters = method.GetParameters();
|
||||
List<object> paramDocs = new List<object>();
|
||||
|
||||
if (parameters.Length > 0)
|
||||
{
|
||||
foreach (var param in parameters)
|
||||
{
|
||||
string paramDesc = GetXmlParamDescription(method, param.Name);
|
||||
|
||||
paramDocs.Add(new
|
||||
{
|
||||
Name = param.Name,
|
||||
Type = GetSimpleTypeName(param.ParameterType),
|
||||
Description = !string.IsNullOrEmpty(paramDesc) ? paramDesc : null
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Información de retorno
|
||||
string returnDesc = GetXmlReturnDescription(method);
|
||||
|
||||
return new
|
||||
{
|
||||
Name = method.Name,
|
||||
Type = "Method",
|
||||
Description = description,
|
||||
Returns = GetSimpleTypeName(method.ReturnType),
|
||||
ReturnDescription = !string.IsNullOrEmpty(returnDesc) ? returnDesc : null,
|
||||
Parameters = paramDocs.Count > 0 ? paramDocs.ToArray() : null,
|
||||
IsStatic = method.IsStatic
|
||||
};
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null; // Si hay error, simplemente no incluir este método
|
||||
}
|
||||
}
|
||||
|
||||
private object DocumentProperty(PropertyInfo property)
|
||||
{
|
||||
try
|
||||
{
|
||||
string description = GetXmlSummary(property);
|
||||
|
||||
return new
|
||||
{
|
||||
Name = property.Name,
|
||||
Type = "Property",
|
||||
Description = description,
|
||||
PropertyType = GetSimpleTypeName(property.PropertyType),
|
||||
CanRead = property.CanRead,
|
||||
CanWrite = property.CanWrite,
|
||||
IsStatic = property.GetAccessors(true).FirstOrDefault()?.IsStatic ?? false
|
||||
};
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private object DocumentField(FieldInfo field)
|
||||
{
|
||||
try
|
||||
{
|
||||
string description = GetXmlSummary(field);
|
||||
|
||||
return new
|
||||
{
|
||||
Name = field.Name,
|
||||
Type = "Field",
|
||||
Description = description,
|
||||
FieldType = GetSimpleTypeName(field.FieldType),
|
||||
IsStatic = field.IsStatic,
|
||||
IsConstant = field.IsLiteral
|
||||
};
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private object DocumentEvent(EventInfo eventInfo)
|
||||
{
|
||||
try
|
||||
{
|
||||
string description = GetXmlSummary(eventInfo);
|
||||
|
||||
return new
|
||||
{
|
||||
Name = eventInfo.Name,
|
||||
Type = "Event",
|
||||
Description = description,
|
||||
EventType = GetSimpleTypeName(eventInfo.EventHandlerType),
|
||||
IsStatic = eventInfo.GetAddMethod()?.IsStatic ?? false
|
||||
};
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Métodos auxiliares
|
||||
|
||||
private string GetTypeKind(Type type)
|
||||
{
|
||||
if (type.IsEnum) return "Enum";
|
||||
if (type.IsInterface) return "Interface";
|
||||
if (type.IsValueType) return "Struct";
|
||||
if (type.IsClass)
|
||||
{
|
||||
if (type.IsAbstract && type.IsSealed) return "Static Class";
|
||||
if (type.IsAbstract) return "Abstract Class";
|
||||
return "Class";
|
||||
}
|
||||
return "Type";
|
||||
}
|
||||
|
||||
private string GetSimpleTypeName(Type type)
|
||||
{
|
||||
if (type == null) return "void";
|
||||
|
||||
// Tipos primitivos
|
||||
if (type == typeof(void)) return "void";
|
||||
if (type == typeof(int)) return "int";
|
||||
if (type == typeof(string)) return "string";
|
||||
if (type == typeof(bool)) return "bool";
|
||||
if (type == typeof(double)) return "double";
|
||||
if (type == typeof(float)) return "float";
|
||||
if (type == typeof(object)) return "object";
|
||||
|
||||
// Arrays
|
||||
if (type.IsArray)
|
||||
{
|
||||
return $"{GetSimpleTypeName(type.GetElementType())}[]";
|
||||
}
|
||||
|
||||
// Genéricos
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
string baseName = type.Name;
|
||||
int tickIndex = baseName.IndexOf('`');
|
||||
if (tickIndex > 0)
|
||||
{
|
||||
baseName = baseName.Substring(0, tickIndex);
|
||||
}
|
||||
|
||||
var argTypes = type.GetGenericArguments()
|
||||
.Select(GetSimpleTypeName)
|
||||
.ToArray();
|
||||
|
||||
return $"{baseName}<{string.Join(", ", argTypes)}>";
|
||||
}
|
||||
|
||||
// Para tipos normales, usar solo el nombre simple
|
||||
return type.Name;
|
||||
}
|
||||
|
||||
// Métodos para extraer documentación XML
|
||||
|
||||
private string GetXmlSummary(MemberInfo member)
|
||||
{
|
||||
if (_xmlDoc == null) return null;
|
||||
|
||||
try
|
||||
{
|
||||
string xmlId = GetXmlDocId(member);
|
||||
var docNode = _xmlDoc.Descendants("member")
|
||||
.FirstOrDefault(m => m.Attribute("name")?.Value == xmlId);
|
||||
|
||||
if (docNode != null)
|
||||
{
|
||||
var summaryNode = docNode.Element("summary");
|
||||
if (summaryNode != null)
|
||||
{
|
||||
return CleanXmlComment(summaryNode.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { /* Ignorar errores */ }
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string GetXmlParamDescription(MethodInfo method, string paramName)
|
||||
{
|
||||
if (_xmlDoc == null) return null;
|
||||
|
||||
try
|
||||
{
|
||||
string xmlId = GetXmlDocId(method);
|
||||
var docNode = _xmlDoc.Descendants("member")
|
||||
.FirstOrDefault(m => m.Attribute("name")?.Value == xmlId);
|
||||
|
||||
if (docNode != null)
|
||||
{
|
||||
var paramNode = docNode.Elements("param")
|
||||
.FirstOrDefault(p => p.Attribute("name")?.Value == paramName);
|
||||
|
||||
if (paramNode != null)
|
||||
{
|
||||
return CleanXmlComment(paramNode.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { /* Ignorar errores */ }
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string GetXmlReturnDescription(MethodInfo method)
|
||||
{
|
||||
if (_xmlDoc == null) return null;
|
||||
|
||||
try
|
||||
{
|
||||
string xmlId = GetXmlDocId(method);
|
||||
var docNode = _xmlDoc.Descendants("member")
|
||||
.FirstOrDefault(m => m.Attribute("name")?.Value == xmlId);
|
||||
|
||||
if (docNode != null)
|
||||
{
|
||||
var returnNode = docNode.Element("returns");
|
||||
if (returnNode != null)
|
||||
{
|
||||
return CleanXmlComment(returnNode.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { /* Ignorar errores */ }
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string GetXmlDocId(MemberInfo member)
|
||||
{
|
||||
string prefix;
|
||||
|
||||
if (member is Type)
|
||||
prefix = "T:";
|
||||
else if (member is MethodInfo)
|
||||
prefix = "M:";
|
||||
else if (member is PropertyInfo)
|
||||
prefix = "P:";
|
||||
else if (member is EventInfo)
|
||||
prefix = "E:";
|
||||
else if (member is FieldInfo)
|
||||
prefix = "F:";
|
||||
else
|
||||
return null;
|
||||
|
||||
// Para tipos
|
||||
if (member is Type type)
|
||||
return $"{prefix}{type.FullName}";
|
||||
|
||||
// Para miembros
|
||||
string declaringType = member.DeclaringType?.FullName ?? "Unknown";
|
||||
return $"{prefix}{declaringType}.{member.Name}";
|
||||
}
|
||||
|
||||
private string CleanXmlComment(string comment)
|
||||
{
|
||||
if (string.IsNullOrEmpty(comment))
|
||||
return null;
|
||||
|
||||
// Eliminar espacios en blanco extra
|
||||
comment = comment.Trim();
|
||||
|
||||
// Convertir saltos de línea y tabulaciones a espacios simples
|
||||
comment = comment.Replace("\r", " ")
|
||||
.Replace("\n", " ")
|
||||
.Replace("\t", " ");
|
||||
|
||||
// Reemplazar múltiples espacios con uno solo
|
||||
while (comment.Contains(" "))
|
||||
comment = comment.Replace(" ", " ");
|
||||
|
||||
return comment;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,431 @@
|
|||
using NetDocsForLLM.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace NetDocsForLLM.Services
|
||||
{
|
||||
public class XmlDocGenerator : IDocFxService
|
||||
{
|
||||
private readonly string _workingDirectory;
|
||||
private TextWriter _logWriter;
|
||||
|
||||
public XmlDocGenerator()
|
||||
{
|
||||
// Crear un directorio temporal para trabajar
|
||||
_workingDirectory = Path.Combine(Path.GetTempPath(), "NetDocsForLLM_" + Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(_workingDirectory);
|
||||
}
|
||||
|
||||
public async Task<string> GenerateMetadataAsync(IEnumerable<AssemblyModel> assemblies)
|
||||
{
|
||||
// Crear directorio para metadatos
|
||||
var metadataPath = Path.Combine(_workingDirectory, "metadata");
|
||||
Directory.CreateDirectory(metadataPath);
|
||||
|
||||
// Crear archivo de log
|
||||
var logPath = Path.Combine(metadataPath, "analysis_log.txt");
|
||||
using (_logWriter = new StreamWriter(logPath, false))
|
||||
{
|
||||
_logWriter.WriteLine($"Iniciando generación de documentación XML: {DateTime.Now}");
|
||||
|
||||
// Para cada ensamblado, generar documentación XML
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
await Task.Run(() => GenerateXmlDocumentation(assembly, metadataPath));
|
||||
}
|
||||
}
|
||||
|
||||
return metadataPath;
|
||||
}
|
||||
|
||||
private void GenerateXmlDocumentation(AssemblyModel assemblyModel, string outputPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
Assembly assembly = assemblyModel.LoadedAssembly;
|
||||
if (assembly == null)
|
||||
{
|
||||
_logWriter.WriteLine($"ERROR: El ensamblado {assemblyModel.Name} no está cargado");
|
||||
return;
|
||||
}
|
||||
|
||||
_logWriter.WriteLine($"\n=== Procesando ensamblado: {assemblyModel.Name} ===");
|
||||
|
||||
// Crear el documento XML
|
||||
XDocument doc = new XDocument(
|
||||
new XDeclaration("1.0", "utf-8", null),
|
||||
new XElement("doc",
|
||||
new XElement("assembly",
|
||||
new XElement("name", assembly.GetName().Name)
|
||||
),
|
||||
new XElement("members")
|
||||
)
|
||||
);
|
||||
|
||||
// Obtener el elemento members para agregar miembros
|
||||
var membersElement = doc.Root.Element("members");
|
||||
|
||||
// Procesar todos los tipos exportados
|
||||
foreach (var type in assembly.GetExportedTypes().Where(t => t.IsPublic))
|
||||
{
|
||||
try
|
||||
{
|
||||
// Generar documentación para el tipo
|
||||
GenerateTypeDocumentation(type, membersElement);
|
||||
|
||||
// Generar documentación para métodos
|
||||
foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)
|
||||
.Where(m => !m.IsSpecialName))
|
||||
{
|
||||
GenerateMethodDocumentation(method, membersElement);
|
||||
}
|
||||
|
||||
// Generar documentación para propiedades
|
||||
foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly))
|
||||
{
|
||||
GeneratePropertyDocumentation(property, membersElement);
|
||||
}
|
||||
|
||||
// Generar documentación para eventos
|
||||
foreach (var eventInfo in type.GetEvents(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly))
|
||||
{
|
||||
GenerateEventDocumentation(eventInfo, membersElement);
|
||||
}
|
||||
|
||||
// Generar documentación para campos
|
||||
foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly))
|
||||
{
|
||||
GenerateFieldDocumentation(field, membersElement);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logWriter.WriteLine($"Error procesando tipo {type.FullName}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// Guardar la documentación XML
|
||||
string xmlFileName = $"{assembly.GetName().Name}.xml";
|
||||
string xmlFilePath = Path.Combine(outputPath, xmlFileName);
|
||||
|
||||
// Guardar con formato
|
||||
using (var writer = new XmlTextWriter(xmlFilePath, Encoding.UTF8))
|
||||
{
|
||||
writer.Formatting = Formatting.Indented;
|
||||
writer.Indentation = 4;
|
||||
doc.Save(writer);
|
||||
}
|
||||
|
||||
_logWriter.WriteLine($"Documentación XML guardada en: {xmlFilePath}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logWriter.WriteLine($"ERROR general procesando ensamblado {assemblyModel.Name}: {ex.Message}");
|
||||
_logWriter.WriteLine(ex.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateTypeDocumentation(Type type, XElement membersElement)
|
||||
{
|
||||
string typeId = $"T:{type.FullName}";
|
||||
var memberElement = new XElement("member", new XAttribute("name", typeId));
|
||||
|
||||
// Agregar summary
|
||||
memberElement.Add(new XElement("summary",
|
||||
$"Represents a {GetTypeKindDescription(type)}: {type.Name}"));
|
||||
|
||||
// Si es una clase que hereda de otra (excepto Object), agregar esa información
|
||||
if (type.IsClass && type.BaseType != null && type.BaseType != typeof(object))
|
||||
{
|
||||
memberElement.Add(new XElement("remarks",
|
||||
$"Inherits from {type.BaseType.Name}"));
|
||||
}
|
||||
|
||||
// Si implementa interfaces, mencionarlas
|
||||
var interfaces = type.GetInterfaces();
|
||||
if (interfaces.Length > 0)
|
||||
{
|
||||
var implementsText = $"Implements: {string.Join(", ", interfaces.Select(i => i.Name))}";
|
||||
|
||||
if (memberElement.Element("remarks") != null)
|
||||
{
|
||||
memberElement.Element("remarks").Value += $"\n{implementsText}";
|
||||
}
|
||||
else
|
||||
{
|
||||
memberElement.Add(new XElement("remarks", implementsText));
|
||||
}
|
||||
}
|
||||
|
||||
membersElement.Add(memberElement);
|
||||
}
|
||||
|
||||
private void GenerateMethodDocumentation(MethodInfo method, XElement membersElement)
|
||||
{
|
||||
try
|
||||
{
|
||||
string methodId = GetMethodXmlId(method);
|
||||
var memberElement = new XElement("member", new XAttribute("name", methodId));
|
||||
|
||||
// Agregar summary básico
|
||||
memberElement.Add(new XElement("summary",
|
||||
$"{method.Name} {GetMethodDescription(method)}"));
|
||||
|
||||
// Agregar parámetros
|
||||
foreach (var param in method.GetParameters())
|
||||
{
|
||||
memberElement.Add(new XElement("param",
|
||||
new XAttribute("name", param.Name),
|
||||
GetParameterDescription(param)));
|
||||
}
|
||||
|
||||
// Agregar returns si no es void
|
||||
if (method.ReturnType != typeof(void))
|
||||
{
|
||||
memberElement.Add(new XElement("returns",
|
||||
$"A {GetSimpleTypeName(method.ReturnType)} value."));
|
||||
}
|
||||
|
||||
membersElement.Add(memberElement);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logWriter.WriteLine($"Error generando documentación para método {method.Name}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void GeneratePropertyDocumentation(PropertyInfo property, XElement membersElement)
|
||||
{
|
||||
try
|
||||
{
|
||||
string propertyId = $"P:{property.DeclaringType.FullName}.{property.Name}";
|
||||
var memberElement = new XElement("member", new XAttribute("name", propertyId));
|
||||
|
||||
// Accesibilidad (get/set)
|
||||
string access = "";
|
||||
if (property.CanRead) access += "get; ";
|
||||
if (property.CanWrite) access += "set; ";
|
||||
|
||||
// Agregar summary
|
||||
memberElement.Add(new XElement("summary",
|
||||
$"Gets{(property.CanWrite ? " or sets" : "")} the {property.Name} property."));
|
||||
|
||||
// Agregar valor
|
||||
memberElement.Add(new XElement("value",
|
||||
$"A {GetSimpleTypeName(property.PropertyType)} representing the {property.Name}."));
|
||||
|
||||
membersElement.Add(memberElement);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logWriter.WriteLine($"Error generando documentación para propiedad {property.Name}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateEventDocumentation(EventInfo eventInfo, XElement membersElement)
|
||||
{
|
||||
try
|
||||
{
|
||||
string eventId = $"E:{eventInfo.DeclaringType.FullName}.{eventInfo.Name}";
|
||||
var memberElement = new XElement("member", new XAttribute("name", eventId));
|
||||
|
||||
// Agregar summary
|
||||
memberElement.Add(new XElement("summary",
|
||||
$"Occurs when {eventInfo.Name} is raised."));
|
||||
|
||||
// Agregar remarks con el tipo del handler
|
||||
memberElement.Add(new XElement("remarks",
|
||||
$"Event handler type: {GetSimpleTypeName(eventInfo.EventHandlerType)}"));
|
||||
|
||||
membersElement.Add(memberElement);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logWriter.WriteLine($"Error generando documentación para evento {eventInfo.Name}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateFieldDocumentation(FieldInfo field, XElement membersElement)
|
||||
{
|
||||
try
|
||||
{
|
||||
string fieldId = $"F:{field.DeclaringType.FullName}.{field.Name}";
|
||||
var memberElement = new XElement("member", new XAttribute("name", fieldId));
|
||||
|
||||
// Modificadores
|
||||
string modifiers = "";
|
||||
if (field.IsStatic) modifiers += "static ";
|
||||
if (field.IsInitOnly) modifiers += "readonly ";
|
||||
if (field.IsLiteral) modifiers += "constant ";
|
||||
|
||||
// Agregar summary
|
||||
memberElement.Add(new XElement("summary",
|
||||
$"Represents the {modifiers}{field.Name} field."));
|
||||
|
||||
membersElement.Add(memberElement);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logWriter.WriteLine($"Error generando documentación para campo {field.Name}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// Métodos auxiliares
|
||||
|
||||
private string GetMethodXmlId(MethodInfo method)
|
||||
{
|
||||
var parameters = method.GetParameters();
|
||||
if (parameters.Length == 0)
|
||||
{
|
||||
return $"M:{method.DeclaringType.FullName}.{method.Name}";
|
||||
}
|
||||
|
||||
var paramTypes = string.Join(",", parameters.Select(p => GetParameterTypeName(p.ParameterType)));
|
||||
return $"M:{method.DeclaringType.FullName}.{method.Name}({paramTypes})";
|
||||
}
|
||||
|
||||
private string GetParameterTypeName(Type type)
|
||||
{
|
||||
// El formato de los parámetros en los IDs XML de documentación es específico
|
||||
|
||||
// Arrays
|
||||
if (type.IsArray)
|
||||
{
|
||||
return $"{GetParameterTypeName(type.GetElementType())}[]";
|
||||
}
|
||||
|
||||
// Genéricos
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
var genericTypeDef = type.GetGenericTypeDefinition();
|
||||
var genericArgs = string.Join(",", type.GetGenericArguments().Select(GetParameterTypeName));
|
||||
|
||||
// Para tipos como List<T>, devolver algo como System.Collections.Generic.List{T}
|
||||
if (genericTypeDef == typeof(System.Collections.Generic.List<>))
|
||||
{
|
||||
return $"System.Collections.Generic.List{{{genericArgs}}}";
|
||||
}
|
||||
|
||||
// Para otros tipos genéricos, usar el nombre completo con {}
|
||||
return $"{genericTypeDef.FullName.Split('`')[0]}{{{genericArgs}}}";
|
||||
}
|
||||
|
||||
// Para tipos normales, usar el nombre completo
|
||||
return type.FullName;
|
||||
}
|
||||
|
||||
private string GetTypeKindDescription(Type type)
|
||||
{
|
||||
if (type.IsEnum) return "enumeration";
|
||||
if (type.IsInterface) return "interface";
|
||||
if (type.IsValueType && !type.IsPrimitive) return "structure";
|
||||
if (type.IsClass)
|
||||
{
|
||||
if (type.IsAbstract && type.IsSealed) return "static class";
|
||||
if (type.IsAbstract) return "abstract class";
|
||||
return "class";
|
||||
}
|
||||
return "type";
|
||||
}
|
||||
|
||||
private string GetMethodDescription(MethodInfo method)
|
||||
{
|
||||
// Crear una descripción básica basada en el nombre y parámetros
|
||||
var parameters = method.GetParameters();
|
||||
|
||||
if (parameters.Length == 0)
|
||||
{
|
||||
if (method.ReturnType == typeof(void))
|
||||
{
|
||||
return "performs an operation.";
|
||||
}
|
||||
return $"gets a {GetSimpleTypeName(method.ReturnType)} value.";
|
||||
}
|
||||
|
||||
return $"with {parameters.Length} parameter{(parameters.Length > 1 ? "s" : "")}.";
|
||||
}
|
||||
|
||||
private string GetParameterDescription(ParameterInfo param)
|
||||
{
|
||||
string desc = $"A {GetSimpleTypeName(param.ParameterType)}";
|
||||
|
||||
if (param.IsOptional)
|
||||
{
|
||||
object defaultValue = param.DefaultValue;
|
||||
string defaultValueStr = defaultValue?.ToString() ?? "null";
|
||||
|
||||
if (param.ParameterType == typeof(string) && defaultValue != null)
|
||||
{
|
||||
defaultValueStr = $"\"{defaultValueStr}\"";
|
||||
}
|
||||
|
||||
desc += $" (Optional, defaults to {defaultValueStr})";
|
||||
}
|
||||
|
||||
if (param.IsOut)
|
||||
{
|
||||
desc += " (Output)";
|
||||
}
|
||||
else if (param.ParameterType.IsByRef)
|
||||
{
|
||||
desc += " (Reference)";
|
||||
}
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
private string GetSimpleTypeName(Type type)
|
||||
{
|
||||
if (type == null) return "void";
|
||||
|
||||
// Si es un tipo por referencia (como out o ref parámetros)
|
||||
if (type.IsByRef)
|
||||
{
|
||||
return GetSimpleTypeName(type.GetElementType());
|
||||
}
|
||||
|
||||
// Tipos primitivos
|
||||
if (type == typeof(void)) return "void";
|
||||
if (type == typeof(int)) return "int";
|
||||
if (type == typeof(string)) return "string";
|
||||
if (type == typeof(bool)) return "bool";
|
||||
if (type == typeof(double)) return "double";
|
||||
if (type == typeof(float)) return "float";
|
||||
if (type == typeof(decimal)) return "decimal";
|
||||
if (type == typeof(object)) return "object";
|
||||
|
||||
// Arrays
|
||||
if (type.IsArray)
|
||||
{
|
||||
return $"{GetSimpleTypeName(type.GetElementType())}[]";
|
||||
}
|
||||
|
||||
// Genéricos
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
string baseName = type.Name;
|
||||
int tickIndex = baseName.IndexOf('`');
|
||||
if (tickIndex > 0)
|
||||
{
|
||||
baseName = baseName.Substring(0, tickIndex);
|
||||
}
|
||||
|
||||
var argTypes = type.GetGenericArguments()
|
||||
.Select(GetSimpleTypeName)
|
||||
.ToArray();
|
||||
|
||||
return $"{baseName}<{string.Join(", ", argTypes)}>";
|
||||
}
|
||||
|
||||
// Para tipos normales, usar solo el nombre simple
|
||||
return type.Name;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue