Primera version funcionante
This commit is contained in:
parent
641bb7baf1
commit
ebbee5414d
|
@ -20,7 +20,7 @@ namespace NetDocsForLLM
|
||||||
private void ConfigureServices(ServiceCollection services)
|
private void ConfigureServices(ServiceCollection services)
|
||||||
{
|
{
|
||||||
// Register services
|
// Register services
|
||||||
services.AddSingleton<IDocFxService, ReflectionAnalyzerService>(); // <- Cambio aquí
|
services.AddSingleton<IDocFxService, XmlDocGenerator>(); // Usar el generador XML
|
||||||
services.AddSingleton<IDocumentationGenerator, DocumentationGenerator>();
|
services.AddSingleton<IDocumentationGenerator, DocumentationGenerator>();
|
||||||
services.AddSingleton<IAssemblyAnalyzer, AssemblyAnalyzer>();
|
services.AddSingleton<IAssemblyAnalyzer, AssemblyAnalyzer>();
|
||||||
|
|
||||||
|
|
|
@ -162,21 +162,16 @@
|
||||||
<Button Content="Copiar al portapapeles"
|
<Button Content="Copiar al portapapeles"
|
||||||
Command="{Binding CopyToClipboardCommand}"
|
Command="{Binding CopyToClipboardCommand}"
|
||||||
Padding="10,5"
|
Padding="10,5"
|
||||||
Margin="0,0,10,0"/>
|
Margin="0,0,10,0"
|
||||||
|
IsEnabled="{Binding HasGeneratedDocumentation}"/>
|
||||||
|
|
||||||
<Button Content="Exportar..."
|
<Button Content="Exportar..."
|
||||||
Command="{Binding ExportDocumentationCommand}"
|
Command="{Binding ExportDocumentationCommand}"
|
||||||
Padding="10,5"/>
|
Padding="10,5"
|
||||||
|
IsEnabled="{Binding HasGeneratedDocumentation}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<xctk:PropertyGrid Grid.Row="1"
|
<!-- Eliminamos el PropertyGrid que estaba en conflicto con el Border -->
|
||||||
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>
|
|
||||||
|
|
||||||
<Border Grid.Row="1" Background="#f5f5f5" BorderBrush="#ddd" BorderThickness="1">
|
<Border Grid.Row="1" Background="#f5f5f5" BorderBrush="#ddd" BorderThickness="1">
|
||||||
<TextBox Text="{Binding DocumentationPreview, Mode=OneWay}"
|
<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.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Xml;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
|
using Formatting = Newtonsoft.Json.Formatting;
|
||||||
|
|
||||||
namespace NetDocsForLLM.Services
|
namespace NetDocsForLLM.Services
|
||||||
{
|
{
|
||||||
public interface IDocumentationGenerator
|
|
||||||
{
|
|
||||||
Task<DocumentationModel> GenerateDocumentation(IEnumerable<AssemblyModel> assemblies, ExportSettings settings);
|
|
||||||
string GenerateDocumentationPreview(DocumentationModel documentation, ExportSettings settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class DocumentationGenerator : IDocumentationGenerator
|
public class DocumentationGenerator : IDocumentationGenerator
|
||||||
{
|
{
|
||||||
private readonly IDocFxService _metadataService;
|
private readonly IDocFxService _metadataService;
|
||||||
|
@ -28,21 +24,18 @@ namespace NetDocsForLLM.Services
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Generate metadata using reflection service
|
// Generate metadata using XmlDocGenerator
|
||||||
var metadataPath = await _metadataService.GenerateMetadataAsync(assemblies);
|
var metadataPath = await _metadataService.GenerateMetadataAsync(assemblies);
|
||||||
|
|
||||||
// Process metadata files
|
// Create a new documentation model
|
||||||
var documentation = new DocumentationModel();
|
var documentation = new DocumentationModel();
|
||||||
var namespaces = new Dictionary<string, NamespaceDocumentation>();
|
var namespaces = new Dictionary<string, NamespaceDocumentation>();
|
||||||
|
|
||||||
// Read JSON files produced by our reflection service
|
// Process XML files produced by our XmlDocGenerator
|
||||||
var jsonFiles = Directory.GetFiles(metadataPath, "*.json", SearchOption.AllDirectories);
|
var xmlFiles = Directory.GetFiles(metadataPath, "*.xml");
|
||||||
foreach (var jsonFile in jsonFiles)
|
foreach (var xmlFile in xmlFiles)
|
||||||
{
|
{
|
||||||
if (Path.GetFileName(jsonFile).StartsWith("error_"))
|
ProcessXmlDocumentation(xmlFile, documentation, namespaces, settings);
|
||||||
continue; // Skip error files
|
|
||||||
|
|
||||||
ProcessJsonMetadata(jsonFile, documentation, namespaces, settings);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return documentation;
|
return documentation;
|
||||||
|
@ -53,108 +46,136 @@ namespace NetDocsForLLM.Services
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProcessJsonMetadata(string jsonFile, DocumentationModel documentation,
|
private void ProcessXmlDocumentation(string xmlFile, DocumentationModel documentation,
|
||||||
Dictionary<string, NamespaceDocumentation> namespaces,
|
Dictionary<string, NamespaceDocumentation> namespaces,
|
||||||
ExportSettings settings)
|
ExportSettings settings)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Leer el archivo JSON
|
// Cargar el archivo XML
|
||||||
var jsonContent = File.ReadAllText(jsonFile);
|
var xmlDoc = XDocument.Load(xmlFile);
|
||||||
var typeData = JsonConvert.DeserializeObject<dynamic>(jsonContent);
|
|
||||||
|
|
||||||
if (typeData == null)
|
// Obtener el nombre del ensamblado
|
||||||
|
string assemblyName = xmlDoc.Root.Element("assembly")?.Element("name")?.Value;
|
||||||
|
if (string.IsNullOrEmpty(assemblyName))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Obtener namespace
|
// Leer todos los miembros
|
||||||
string namespaceName = typeData.Namespace?.ToString() ?? "Global";
|
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))
|
if (!namespaces.TryGetValue(namespaceName, out var namespaceDoc))
|
||||||
{
|
{
|
||||||
namespaceDoc = new NamespaceDocumentation
|
namespaceDoc = new NamespaceDocumentation
|
||||||
{
|
{
|
||||||
Name = namespaceName,
|
Name = namespaceName,
|
||||||
Description = $"Contiene tipos del ensamblado"
|
Description = $"Contains types from the assembly"
|
||||||
};
|
};
|
||||||
namespaces[namespaceName] = namespaceDoc;
|
namespaces[namespaceName] = namespaceDoc;
|
||||||
documentation.Namespaces.Add(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
|
// Crear documentación de tipo
|
||||||
var typeDoc = new TypeDocumentation
|
var typeDoc = new TypeDocumentation
|
||||||
{
|
{
|
||||||
Name = typeData.Name,
|
Name = typeName,
|
||||||
FullName = typeData.FullName,
|
FullName = fullTypeName,
|
||||||
Description = typeData.XmlDocumentation?.Summary ?? "Sin documentación disponible",
|
Description = description,
|
||||||
TypeKind = GetTypeKind(typeData)
|
TypeKind = typeKind
|
||||||
};
|
};
|
||||||
|
|
||||||
// Agregar base types e interfaces si están disponibles
|
// Procesar herencia e interfaces si están disponibles en remarks
|
||||||
if (typeData.BaseType != null && typeData.BaseType.ToString() != "System.Object")
|
if (!string.IsNullOrEmpty(remarks))
|
||||||
{
|
{
|
||||||
typeDoc.BaseTypes.Add(typeData.BaseType.ToString());
|
if (remarks.Contains("Inherits from"))
|
||||||
}
|
|
||||||
|
|
||||||
// Agregar interfaces
|
|
||||||
if (typeData.Interfaces != null)
|
|
||||||
{
|
|
||||||
foreach (var interfaceType in typeData.Interfaces)
|
|
||||||
{
|
{
|
||||||
typeDoc.Interfaces.Add(interfaceType.ToString());
|
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);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Procesar miembros
|
if (remarks.Contains("Implements:"))
|
||||||
if (typeData.Members != null)
|
|
||||||
{
|
|
||||||
foreach (var member in typeData.Members)
|
|
||||||
{
|
{
|
||||||
// Filtrar miembros privados si la configuración lo indica
|
int start = remarks.IndexOf("Implements:") + "Implements:".Length;
|
||||||
if (!settings.IncludePrivateMembers &&
|
int end = remarks.IndexOf("\n", start);
|
||||||
member.IsPublic != null && !(bool)member.IsPublic)
|
if (end < 0) end = remarks.Length;
|
||||||
continue;
|
|
||||||
|
|
||||||
var memberDoc = new MemberDocumentation
|
string interfacesStr = remarks.Substring(start, end - start).Trim();
|
||||||
|
string[] interfaces = interfacesStr.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
foreach (var interfaceName in interfaces)
|
||||||
{
|
{
|
||||||
Name = member.Name,
|
typeDoc.Interfaces.Add(interfaceName.Trim());
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,102 +183,197 @@ namespace NetDocsForLLM.Services
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// Si hay error al procesar, lo registramos pero continuamos
|
Console.WriteLine($"Error procesando tipo: {ex.Message}");
|
||||||
Console.WriteLine($"Error al procesar {jsonFile}: {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)
|
try
|
||||||
return "Class";
|
|
||||||
if (typeData.IsInterface != null && (bool)typeData.IsInterface)
|
|
||||||
return "Interface";
|
|
||||||
if (typeData.IsEnum != null && (bool)typeData.IsEnum)
|
|
||||||
return "Enum";
|
|
||||||
return "Type";
|
|
||||||
}
|
|
||||||
|
|
||||||
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";
|
|
||||||
|
|
||||||
if (memberType == "Method")
|
|
||||||
{
|
{
|
||||||
string returnType = member.ReturnType?.ToString() ?? "void";
|
string memberId = memberElement.Attribute("name")?.Value;
|
||||||
string parameters = "";
|
if (string.IsNullOrEmpty(memberId))
|
||||||
|
return;
|
||||||
|
|
||||||
if (member.Parameters != null)
|
// Determinar el tipo de miembro
|
||||||
|
char memberTypeChar = memberId[0];
|
||||||
|
if (memberId.Length < 3 || memberId[1] != ':')
|
||||||
|
return;
|
||||||
|
|
||||||
|
string memberType;
|
||||||
|
switch (memberTypeChar)
|
||||||
{
|
{
|
||||||
var paramList = new List<string>();
|
case 'M': memberType = "Method"; break;
|
||||||
foreach (var param in member.Parameters)
|
case 'P': memberType = "Property"; break;
|
||||||
{
|
case 'F': memberType = "Field"; break;
|
||||||
string paramType = param.Type?.ToString() ?? "object";
|
case 'E': memberType = "Event"; break;
|
||||||
string paramName = param.Name?.ToString() ?? "param";
|
default: return; // No es un miembro que procesamos
|
||||||
paramList.Add($"{paramType} {paramName}");
|
|
||||||
}
|
|
||||||
parameters = string.Join(", ", paramList);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $"{returnType} {name}({parameters})";
|
// 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")
|
||||||
|
{
|
||||||
|
foreach (var paramElement in memberElement.Elements("param"))
|
||||||
|
{
|
||||||
|
string paramName = paramElement.Attribute("name")?.Value;
|
||||||
|
string paramDesc = paramElement.Value?.Trim();
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(paramName))
|
||||||
|
{
|
||||||
|
// Extraer tipo del parámetro de la descripción
|
||||||
|
string paramType = "object";
|
||||||
|
if (!string.IsNullOrEmpty(paramDesc) && paramDesc.StartsWith("A "))
|
||||||
|
{
|
||||||
|
int spacePos = paramDesc.IndexOf(' ', 2);
|
||||||
|
if (spacePos > 0)
|
||||||
|
{
|
||||||
|
paramType = paramDesc.Substring(2, spacePos - 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
{
|
||||||
|
// Generar firma para propiedades
|
||||||
|
string propType = memberDoc.ReturnType ?? "object";
|
||||||
|
string accessors = "";
|
||||||
|
|
||||||
|
if (description.Contains("Gets"))
|
||||||
|
{
|
||||||
|
accessors += "get; ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (description.Contains("sets"))
|
||||||
|
{
|
||||||
|
accessors += "set; ";
|
||||||
|
}
|
||||||
|
|
||||||
|
memberDoc.Signature = $"{propType} {memberName} {{ {accessors}}}";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Firma simple para otros miembros
|
||||||
|
memberDoc.Signature = $"{memberDoc.ReturnType ?? "void"} {memberName}";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Agregar el miembro al tipo
|
||||||
|
typeDoc.Members.Add(memberDoc);
|
||||||
}
|
}
|
||||||
else if (memberType == "Property")
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
string propType = member.Type?.ToString() ?? "object";
|
Console.WriteLine($"Error procesando miembro: {ex.Message}");
|
||||||
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")
|
|
||||||
{
|
|
||||||
string fieldType = member.Type?.ToString() ?? "object";
|
|
||||||
return $"{fieldType} {name}";
|
|
||||||
}
|
|
||||||
else if (memberType == "Event")
|
|
||||||
{
|
|
||||||
string eventType = member.EventHandlerType?.ToString() ?? "EventHandler";
|
|
||||||
return $"event {eventType} {name}";
|
|
||||||
}
|
|
||||||
|
|
||||||
return name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetReturnType(dynamic member)
|
private string DeriveReturnType(string memberType, string returns, string value)
|
||||||
{
|
{
|
||||||
string memberType = member.MemberType?.ToString();
|
// Intentar extraer el tipo de retorno de las etiquetas returns o value
|
||||||
|
if (!string.IsNullOrEmpty(returns))
|
||||||
if (memberType == "Method")
|
|
||||||
return member.ReturnType?.ToString() ?? "void";
|
|
||||||
else if (memberType == "Property")
|
|
||||||
return member.Type?.ToString() ?? "object";
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetParameterDescription(dynamic xmlDocumentation, string paramName)
|
|
||||||
{
|
|
||||||
if (xmlDocumentation?.Parameters == null || paramName == null)
|
|
||||||
return "";
|
|
||||||
|
|
||||||
foreach (var param in xmlDocumentation.Parameters)
|
|
||||||
{
|
{
|
||||||
if (param.Name?.ToString() == paramName)
|
// Intentar extraer de algo como "A string value."
|
||||||
return param.Description?.ToString() ?? "";
|
if (returns.StartsWith("A ") || returns.StartsWith("An "))
|
||||||
|
{
|
||||||
|
int spacePos = returns.IndexOf(' ', 2);
|
||||||
|
if (spacePos > 0)
|
||||||
|
{
|
||||||
|
return returns.Substring(2, spacePos - 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return "";
|
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)
|
public string GenerateDocumentationPreview(DocumentationModel documentation, ExportSettings settings)
|
||||||
|
@ -276,14 +392,140 @@ namespace NetDocsForLLM.Services
|
||||||
else // YAML
|
else // YAML
|
||||||
{
|
{
|
||||||
// En un escenario real, usaríamos YamlDotNet
|
// En un escenario real, usaríamos YamlDotNet
|
||||||
// Para esta implementación, usaremos una conversión manual simplificada
|
// Para esta implementación, usaremos una conversión manual simplificada a XML
|
||||||
return ConvertJsonToSimpleYaml(
|
var doc = new XDocument(
|
||||||
JsonConvert.SerializeObject(documentation, Formatting.None,
|
new XDeclaration("1.0", "utf-8", null),
|
||||||
new JsonSerializerSettings
|
new XElement("documentation",
|
||||||
|
new XElement("namespaces")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
var namespacesElement = doc.Root.Element("namespaces");
|
||||||
|
|
||||||
|
foreach (var ns in documentation.Namespaces)
|
||||||
|
{
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
|
var typeElement = new XElement("type",
|
||||||
NullValueHandling = NullValueHandling.Ignore
|
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)
|
catch (Exception ex)
|
||||||
|
@ -291,84 +533,5 @@ namespace NetDocsForLLM.Services
|
||||||
throw new InvalidOperationException($"Error al generar vista previa: {ex.Message}", ex);
|
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