Version xon XML funcionando. Revisar si se estan encontrando todos los miembros
This commit is contained in:
parent
ebbee5414d
commit
3282dd8f2d
|
@ -19,8 +19,8 @@ namespace NetDocsForLLM
|
|||
|
||||
private void ConfigureServices(ServiceCollection services)
|
||||
{
|
||||
// Register services
|
||||
services.AddSingleton<IDocFxService, XmlDocGenerator>(); // Usar el generador XML
|
||||
// Registramos el XmlDocGenerator sin dependencia de LogService
|
||||
services.AddSingleton<IDocFxService, XmlDocGenerator>();
|
||||
services.AddSingleton<IDocumentationGenerator, DocumentationGenerator>();
|
||||
services.AddSingleton<IAssemblyAnalyzer, AssemblyAnalyzer>();
|
||||
|
||||
|
|
206
MainWindow.xaml
206
MainWindow.xaml
|
@ -1,37 +1,29 @@
|
|||
<Window x:Class="NetDocsForLLM.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
<Window x:Class="NetDocsForLLM.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:NetDocsForLLM"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:NetDocsForLLM"
|
||||
xmlns:views="clr-namespace:NetDocsForLLM.Views" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
|
||||
xmlns:conv="clr-namespace:NetDocsForLLM.Converters"
|
||||
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
|
||||
mc:Ignorable="d"
|
||||
Title="NetDocs para LLMs" Height="650" Width="800">
|
||||
|
||||
mc:Ignorable="d" Title="NetDocs para LLMs" Height="650" Width="800">
|
||||
|
||||
<Window.Resources>
|
||||
<conv:EnumBooleanConverter x:Key="EnumBooleanConverter"/>
|
||||
</Window.Resources>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Encabezado -->
|
||||
<Border Background="#2c3e50" Padding="15">
|
||||
<StackPanel>
|
||||
<TextBlock Text="Generador de Documentación .NET para LLMs"
|
||||
Foreground="White"
|
||||
FontSize="22"
|
||||
HorizontalAlignment="Center"/>
|
||||
<TextBlock Text="Generador de Documentación .NET para LLMs" Foreground="White" FontSize="22"
|
||||
HorizontalAlignment="Center" />
|
||||
<TextBlock Text="Extraiga y estructure documentación de librerías .NET para uso con modelos de lenguaje"
|
||||
Foreground="#ecf0f1"
|
||||
FontSize="12"
|
||||
HorizontalAlignment="Center"
|
||||
Margin="0,5,0,0"/>
|
||||
Foreground="#ecf0f1" FontSize="12" HorizontalAlignment="Center" Margin="0,5,0,0" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
|
@ -40,54 +32,40 @@
|
|||
<TabItem Header="Selección de Librería">
|
||||
<Grid Margin="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Button Content="Seleccionar Archivos DLL..."
|
||||
Command="{Binding SelectAssembliesCommand}"
|
||||
Padding="10,5"
|
||||
Margin="0,0,0,10"/>
|
||||
<Button Content="Seleccionar Archivos DLL..." Command="{Binding SelectAssembliesCommand}"
|
||||
Padding="10,5" Margin="0,0,0,10" />
|
||||
|
||||
<TextBlock Grid.Row="1"
|
||||
Text="Librerías seleccionadas:"
|
||||
FontWeight="Bold"
|
||||
Margin="0,10,0,5"/>
|
||||
<TextBlock Grid.Row="1" Text="Librerías seleccionadas:" FontWeight="Bold" Margin="0,10,0,5" />
|
||||
|
||||
<ListView Grid.Row="2"
|
||||
ItemsSource="{Binding SelectedAssemblies}"
|
||||
Margin="0,5">
|
||||
<ListView Grid.Row="2" ItemsSource="{Binding SelectedAssemblies}" Margin="0,5">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Orientation="Vertical">
|
||||
<TextBlock Text="{Binding Name}" FontWeight="Bold"/>
|
||||
<TextBlock Text="{Binding FilePath}" FontSize="11" TextWrapping="Wrap"/>
|
||||
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
|
||||
<TextBlock Text="{Binding FilePath}" FontSize="11" TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
<Button Grid.Column="1"
|
||||
Content="X"
|
||||
<Button Grid.Column="1" Content="X"
|
||||
Command="{Binding DataContext.RemoveAssemblyCommand, RelativeSource={RelativeSource AncestorType=ListView}}"
|
||||
CommandParameter="{Binding}"
|
||||
Margin="5,0,0,0"/>
|
||||
CommandParameter="{Binding}" Margin="5,0,0,0" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
|
||||
<Button Grid.Row="3"
|
||||
Content="Analizar y Generar Documentación"
|
||||
Command="{Binding GenerateDocumentationCommand}"
|
||||
IsEnabled="{Binding HasSelectedAssemblies}"
|
||||
Padding="10,5"
|
||||
Margin="0,10,0,0"
|
||||
Background="#2980b9"
|
||||
Foreground="White"/>
|
||||
<Button Grid.Row="3" Content="Analizar y Generar Documentación"
|
||||
Command="{Binding GenerateDocumentationCommand}" IsEnabled="{Binding HasSelectedAssemblies}"
|
||||
Padding="10,5" Margin="0,10,0,0" Background="#2980b9" Foreground="White" />
|
||||
</Grid>
|
||||
</TabItem>
|
||||
|
||||
|
@ -96,54 +74,49 @@
|
|||
<StackPanel Margin="10">
|
||||
<GroupBox Header="Opciones de documentación">
|
||||
<StackPanel Margin="5">
|
||||
<CheckBox Content="Incluir métodos privados"
|
||||
IsChecked="{Binding Settings.IncludePrivateMembers}"
|
||||
Margin="0,5"/>
|
||||
|
||||
<CheckBox Content="Incluir miembros heredados"
|
||||
IsChecked="{Binding Settings.IncludeInheritedMembers}"
|
||||
Margin="0,5"/>
|
||||
|
||||
<CheckBox Content="Incluir ejemplos de código"
|
||||
IsChecked="{Binding Settings.IncludeExamples}"
|
||||
Margin="0,5"/>
|
||||
|
||||
<CheckBox Content="Procesamiento detallado de comentarios XML"
|
||||
IsChecked="{Binding Settings.DetailedXmlComments}"
|
||||
Margin="0,5"/>
|
||||
<CheckBox Content="Incluir métodos privados"
|
||||
IsChecked="{Binding Settings.IncludePrivateMembers}" Margin="0,5" />
|
||||
|
||||
<CheckBox Content="Incluir miembros heredados"
|
||||
IsChecked="{Binding Settings.IncludeInheritedMembers}" Margin="0,5" />
|
||||
|
||||
<CheckBox Content="Incluir ejemplos de código"
|
||||
IsChecked="{Binding Settings.IncludeExamples}" Margin="0,5" />
|
||||
|
||||
<CheckBox Content="Procesamiento detallado de comentarios XML"
|
||||
IsChecked="{Binding Settings.DetailedXmlComments}" Margin="0,5" />
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
|
||||
|
||||
<GroupBox Header="Nivel de detalle" Margin="0,10,0,0">
|
||||
<StackPanel Margin="5">
|
||||
<RadioButton Content="Básico - Solo información esencial"
|
||||
IsChecked="{Binding Settings.DetailLevel, Converter={StaticResource EnumBooleanConverter}, ConverterParameter=Basic}"
|
||||
GroupName="DetailLevel"
|
||||
Margin="0,5"/>
|
||||
|
||||
<RadioButton Content="Estándar - Equilibrio entre detalle y tamaño"
|
||||
IsChecked="{Binding Settings.DetailLevel, Converter={StaticResource EnumBooleanConverter}, ConverterParameter=Standard}"
|
||||
GroupName="DetailLevel"
|
||||
Margin="0,5"/>
|
||||
|
||||
<RadioButton Content="Completo - Toda la información disponible"
|
||||
IsChecked="{Binding Settings.DetailLevel, Converter={StaticResource EnumBooleanConverter}, ConverterParameter=Full}"
|
||||
GroupName="DetailLevel"
|
||||
Margin="0,5"/>
|
||||
<RadioButton Content="Básico - Solo información esencial"
|
||||
IsChecked="{Binding Settings.DetailLevel, Converter={StaticResource EnumBooleanConverter}, ConverterParameter=Basic}"
|
||||
GroupName="DetailLevel" Margin="0,5" />
|
||||
|
||||
<RadioButton Content="Estándar - Equilibrio entre detalle y tamaño"
|
||||
IsChecked="{Binding Settings.DetailLevel, Converter={StaticResource EnumBooleanConverter}, ConverterParameter=Standard}"
|
||||
GroupName="DetailLevel" Margin="0,5" />
|
||||
|
||||
<RadioButton Content="Completo - Toda la información disponible"
|
||||
IsChecked="{Binding Settings.DetailLevel, Converter={StaticResource EnumBooleanConverter}, ConverterParameter=Full}"
|
||||
GroupName="DetailLevel" Margin="0,5" />
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
|
||||
|
||||
<GroupBox Header="Formato de salida" Margin="0,10,0,0">
|
||||
<StackPanel Margin="5">
|
||||
<RadioButton Content="JSON"
|
||||
IsChecked="{Binding Settings.OutputFormat, Converter={StaticResource EnumBooleanConverter}, ConverterParameter=Json}"
|
||||
GroupName="OutputFormat"
|
||||
Margin="0,5"/>
|
||||
|
||||
<RadioButton Content="YAML"
|
||||
IsChecked="{Binding Settings.OutputFormat, Converter={StaticResource EnumBooleanConverter}, ConverterParameter=Yaml}"
|
||||
GroupName="OutputFormat"
|
||||
Margin="0,5"/>
|
||||
<RadioButton Content="JSON"
|
||||
IsChecked="{Binding Settings.OutputFormat, Converter={StaticResource EnumBooleanConverter}, ConverterParameter=Json}"
|
||||
GroupName="OutputFormat" Margin="0,5" />
|
||||
|
||||
<RadioButton Content="XML (Recomendado para LLMs)"
|
||||
IsChecked="{Binding Settings.OutputFormat, Converter={StaticResource EnumBooleanConverter}, ConverterParameter=Xml}"
|
||||
GroupName="OutputFormat" Margin="0,5" />
|
||||
|
||||
<RadioButton Content="YAML"
|
||||
IsChecked="{Binding Settings.OutputFormat, Converter={StaticResource EnumBooleanConverter}, ConverterParameter=Yaml}"
|
||||
GroupName="OutputFormat" Margin="0,5" />
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
|
@ -153,51 +126,42 @@
|
|||
<TabItem Header="Previsualización" IsEnabled="{Binding HasGeneratedDocumentation}">
|
||||
<Grid Margin="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
|
||||
<Button Content="Copiar al portapapeles"
|
||||
Command="{Binding CopyToClipboardCommand}"
|
||||
Padding="10,5"
|
||||
Margin="0,0,10,0"
|
||||
IsEnabled="{Binding HasGeneratedDocumentation}"/>
|
||||
|
||||
<Button Content="Exportar..."
|
||||
Command="{Binding ExportDocumentationCommand}"
|
||||
Padding="10,5"
|
||||
IsEnabled="{Binding HasGeneratedDocumentation}"/>
|
||||
<Button Content="Copiar al portapapeles" Command="{Binding CopyToClipboardCommand}"
|
||||
Padding="10,5" Margin="0,0,10,0" />
|
||||
|
||||
<Button Content="Exportar..." Command="{Binding ExportDocumentationCommand}" Padding="10,5" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- 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}"
|
||||
IsReadOnly="True"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
HorizontalScrollBarVisibility="Auto"
|
||||
FontFamily="Consolas"
|
||||
AcceptsReturn="True"
|
||||
Padding="10"/>
|
||||
<TextBox Text="{Binding DocumentationPreview, Mode=OneWay}" IsReadOnly="True"
|
||||
VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto"
|
||||
FontFamily="Consolas" AcceptsReturn="True" Padding="10" />
|
||||
</Border>
|
||||
|
||||
<TextBlock Grid.Row="2"
|
||||
Text="{Binding DocumentationStats}"
|
||||
Margin="0,10,0,0"/>
|
||||
|
||||
<TextBlock Grid.Row="2" Text="{Binding DocumentationStats}" Margin="0,10,0,0" />
|
||||
</Grid>
|
||||
</TabItem>
|
||||
|
||||
<!-- Nueva pestaña de Log -->
|
||||
<TabItem Header="Log de Análisis">
|
||||
<views:LogView />
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
|
||||
<!-- Pie de página -->
|
||||
<StatusBar Grid.Row="2">
|
||||
<StatusBarItem>
|
||||
<TextBlock Text="{Binding StatusMessage}"/>
|
||||
<TextBlock Text="{Binding StatusMessage}" />
|
||||
</StatusBarItem>
|
||||
<StatusBarItem HorizontalAlignment="Right">
|
||||
<ProgressBar Width="100" Height="15" IsIndeterminate="{Binding IsProcessing}"/>
|
||||
<ProgressBar Width="100" Height="15" IsIndeterminate="{Binding IsProcessing}" />
|
||||
</StatusBarItem>
|
||||
</StatusBar>
|
||||
</Grid>
|
||||
</Window>
|
||||
</Window>
|
|
@ -12,6 +12,7 @@ namespace NetDocsForLLM.Models
|
|||
public enum OutputFormat
|
||||
{
|
||||
Json,
|
||||
Xml,
|
||||
Yaml
|
||||
}
|
||||
|
||||
|
@ -68,7 +69,7 @@ namespace NetDocsForLLM.Models
|
|||
_includeExamples = true;
|
||||
_detailedXmlComments = true;
|
||||
_detailLevel = DetailLevel.Standard;
|
||||
_outputFormat = OutputFormat.Json;
|
||||
_outputFormat = OutputFormat.Xml; // Cambiado a XML por defecto
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,182 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace NetDocsForLLM.Services
|
||||
{
|
||||
public enum LogLevel
|
||||
{
|
||||
Error = 0,
|
||||
Warning = 1,
|
||||
Info = 2,
|
||||
Debug = 3
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clase estática para logs que no depende de inyección de dependencias
|
||||
/// </summary>
|
||||
public static class AppLogger
|
||||
{
|
||||
private static readonly List<LogEntry> _logEntries = new List<LogEntry>();
|
||||
private static int _errorCount = 0;
|
||||
private static int _warningCount = 0;
|
||||
private static int _typeCount = 0;
|
||||
private static int _memberCount = 0;
|
||||
private static string _logFilePath;
|
||||
private static StreamWriter _fileWriter;
|
||||
|
||||
// Evento que se dispara cuando hay nuevos mensajes
|
||||
public static event EventHandler LogUpdated;
|
||||
|
||||
static AppLogger()
|
||||
{
|
||||
// Configurar archivo de log
|
||||
string tempFolder = Path.Combine(Path.GetTempPath(), "NetDocsForLLM_" + DateTime.Now.ToString("yyyyMMdd_HHmmss"));
|
||||
Directory.CreateDirectory(tempFolder);
|
||||
_logFilePath = Path.Combine(tempFolder, "app_log.txt");
|
||||
_fileWriter = new StreamWriter(_logFilePath, true) { AutoFlush = true };
|
||||
|
||||
LogInfo($"Log inicializado en: {_logFilePath}");
|
||||
}
|
||||
|
||||
public static void LogDebug(string message)
|
||||
{
|
||||
AddLogEntry(LogLevel.Debug, message);
|
||||
}
|
||||
|
||||
public static void LogInfo(string message)
|
||||
{
|
||||
AddLogEntry(LogLevel.Info, message);
|
||||
}
|
||||
|
||||
public static void LogWarning(string message)
|
||||
{
|
||||
_warningCount++;
|
||||
AddLogEntry(LogLevel.Warning, message);
|
||||
}
|
||||
|
||||
public static void LogError(string message)
|
||||
{
|
||||
_errorCount++;
|
||||
AddLogEntry(LogLevel.Error, message);
|
||||
}
|
||||
|
||||
public static void LogException(Exception ex)
|
||||
{
|
||||
_errorCount++;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine($"Excepción: {ex.Message}");
|
||||
|
||||
if (ex.StackTrace != null)
|
||||
{
|
||||
sb.AppendLine("Stack Trace:");
|
||||
sb.AppendLine(ex.StackTrace);
|
||||
}
|
||||
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
sb.AppendLine($"Inner Exception: {ex.InnerException.Message}");
|
||||
}
|
||||
|
||||
AddLogEntry(LogLevel.Error, sb.ToString());
|
||||
}
|
||||
|
||||
public static int ErrorCount => _errorCount;
|
||||
public static int WarningCount => _warningCount;
|
||||
public static int TypeCount => _typeCount;
|
||||
public static int MemberCount => _memberCount;
|
||||
|
||||
public static void IncrementTypeCount()
|
||||
{
|
||||
_typeCount++;
|
||||
NotifyLogUpdated();
|
||||
}
|
||||
|
||||
public static void IncrementMemberCount()
|
||||
{
|
||||
_memberCount++;
|
||||
NotifyLogUpdated();
|
||||
}
|
||||
|
||||
private static void AddLogEntry(LogLevel level, string message)
|
||||
{
|
||||
var entry = new LogEntry
|
||||
{
|
||||
Timestamp = DateTime.Now,
|
||||
Level = level,
|
||||
Message = message
|
||||
};
|
||||
|
||||
_logEntries.Add(entry);
|
||||
|
||||
// Escribir al archivo de log
|
||||
string levelText = GetLevelText(level);
|
||||
string logMessage = $"[{entry.Timestamp:yyyy-MM-dd HH:mm:ss}] [{levelText}] {message}";
|
||||
|
||||
_fileWriter.WriteLine(logMessage);
|
||||
|
||||
// Notificar a los escuchadores
|
||||
NotifyLogUpdated();
|
||||
}
|
||||
|
||||
private static void NotifyLogUpdated()
|
||||
{
|
||||
// Invocar el evento en el hilo de la UI si está disponible
|
||||
LogUpdated?.Invoke(null, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public static string GetContent(LogLevel maxLevel = LogLevel.Debug)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
foreach (var entry in _logEntries)
|
||||
{
|
||||
if (entry.Level <= maxLevel)
|
||||
{
|
||||
string levelText = GetLevelText(entry.Level);
|
||||
sb.AppendLine($"[{entry.Timestamp:yyyy-MM-dd HH:mm:ss}] [{levelText}] {entry.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string GetLevelText(LogLevel level)
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
case LogLevel.Error:
|
||||
return "ERROR";
|
||||
case LogLevel.Warning:
|
||||
return "WARN";
|
||||
case LogLevel.Info:
|
||||
return "INFO";
|
||||
case LogLevel.Debug:
|
||||
return "DEBUG";
|
||||
default:
|
||||
return level.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public static void Clear()
|
||||
{
|
||||
_logEntries.Clear();
|
||||
_errorCount = 0;
|
||||
_warningCount = 0;
|
||||
|
||||
// No reiniciar los contadores de tipos y miembros
|
||||
// ya que representan el progreso del análisis
|
||||
|
||||
NotifyLogUpdated();
|
||||
}
|
||||
|
||||
private class LogEntry
|
||||
{
|
||||
public DateTime Timestamp { get; set; }
|
||||
public LogLevel Level { get; set; }
|
||||
public string Message { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -380,152 +380,22 @@ namespace NetDocsForLLM.Services
|
|||
{
|
||||
try
|
||||
{
|
||||
if (settings.OutputFormat == OutputFormat.Json)
|
||||
switch (settings.OutputFormat)
|
||||
{
|
||||
return JsonConvert.SerializeObject(documentation, Formatting.Indented,
|
||||
new JsonSerializerSettings
|
||||
{
|
||||
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
|
||||
NullValueHandling = NullValueHandling.Ignore
|
||||
});
|
||||
}
|
||||
else // YAML
|
||||
{
|
||||
// En un escenario real, usaríamos YamlDotNet
|
||||
// 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)
|
||||
{
|
||||
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)
|
||||
case OutputFormat.Json:
|
||||
return JsonConvert.SerializeObject(documentation, Formatting.Indented,
|
||||
new JsonSerializerSettings
|
||||
{
|
||||
var baseTypesElement = new XElement("baseTypes");
|
||||
foreach (var baseType in type.BaseTypes)
|
||||
{
|
||||
baseTypesElement.Add(new XElement("baseType", baseType));
|
||||
}
|
||||
typeElement.Add(baseTypesElement);
|
||||
}
|
||||
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
|
||||
NullValueHandling = NullValueHandling.Ignore
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
case OutputFormat.Xml:
|
||||
return GenerateXmlOutput(documentation);
|
||||
|
||||
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();
|
||||
}
|
||||
case OutputFormat.Yaml:
|
||||
default:
|
||||
return GenerateYamlOutput(documentation);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -533,5 +403,265 @@ namespace NetDocsForLLM.Services
|
|||
throw new InvalidOperationException($"Error al generar vista previa: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GenerateXmlOutput(DocumentationModel documentation)
|
||||
{
|
||||
var doc = new XDocument(
|
||||
new XDeclaration("1.0", "utf-8", null),
|
||||
new XElement("doc",
|
||||
new XElement("assembly",
|
||||
new XElement("name", "DocumentationForLLM")
|
||||
),
|
||||
new XElement("members")
|
||||
)
|
||||
);
|
||||
|
||||
var membersElement = doc.Root.Element("members");
|
||||
|
||||
// Agregar tipos
|
||||
foreach (var ns in documentation.Namespaces)
|
||||
{
|
||||
foreach (var type in ns.Types)
|
||||
{
|
||||
// Agregar elemento de tipo
|
||||
var typeMember = new XElement("member",
|
||||
new XAttribute("name", $"T:{type.FullName}"),
|
||||
new XElement("summary", type.Description)
|
||||
);
|
||||
|
||||
// Agregar información de base y interfaces si corresponde
|
||||
if (type.BaseTypes.Count > 0 || type.Interfaces.Count > 0)
|
||||
{
|
||||
var remarks = new XElement("remarks");
|
||||
|
||||
if (type.BaseTypes.Count > 0)
|
||||
{
|
||||
remarks.Add(new XText($"Inherits from {type.BaseTypes[0]}"));
|
||||
}
|
||||
|
||||
if (type.Interfaces.Count > 0)
|
||||
{
|
||||
if (type.BaseTypes.Count > 0)
|
||||
remarks.Add(new XText("\n"));
|
||||
|
||||
remarks.Add(new XText($"Implements: {string.Join(", ", type.Interfaces)}"));
|
||||
}
|
||||
|
||||
typeMember.Add(remarks);
|
||||
}
|
||||
|
||||
membersElement.Add(typeMember);
|
||||
|
||||
// Agregar miembros
|
||||
foreach (var member in type.Members)
|
||||
{
|
||||
XElement memberElement;
|
||||
|
||||
// Agregar con formato correcto según el tipo
|
||||
switch (member.MemberType)
|
||||
{
|
||||
case "Method":
|
||||
memberElement = CreateMethodElement(type, member);
|
||||
break;
|
||||
case "Property":
|
||||
memberElement = CreatePropertyElement(type, member);
|
||||
break;
|
||||
case "Event":
|
||||
memberElement = CreateEventElement(type, member);
|
||||
break;
|
||||
case "Field":
|
||||
memberElement = CreateFieldElement(type, member);
|
||||
break;
|
||||
default:
|
||||
continue; // Tipo no soportado
|
||||
}
|
||||
|
||||
membersElement.Add(memberElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Usar un StringWriter para convertir el XML a texto
|
||||
using (var stringWriter = new StringWriter())
|
||||
{
|
||||
using (var writer = new XmlTextWriter(stringWriter))
|
||||
{
|
||||
writer.Formatting = (System.Xml.Formatting)Formatting.Indented;
|
||||
writer.Indentation = 4;
|
||||
doc.Save(writer);
|
||||
}
|
||||
return stringWriter.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
private XElement CreateMethodElement(TypeDocumentation type, MemberDocumentation method)
|
||||
{
|
||||
// Crear ID XML para el método
|
||||
string methodId;
|
||||
|
||||
if (method.Parameters.Count > 0)
|
||||
{
|
||||
var paramTypes = string.Join(",", method.Parameters.Select(p => p.Type));
|
||||
methodId = $"M:{type.FullName}.{method.Name}({paramTypes})";
|
||||
}
|
||||
else
|
||||
{
|
||||
methodId = $"M:{type.FullName}.{method.Name}";
|
||||
}
|
||||
|
||||
var element = new XElement("member",
|
||||
new XAttribute("name", methodId),
|
||||
new XElement("summary", method.Description)
|
||||
);
|
||||
|
||||
// Agregar parámetros
|
||||
foreach (var param in method.Parameters)
|
||||
{
|
||||
element.Add(new XElement("param",
|
||||
new XAttribute("name", param.Name),
|
||||
new XText(param.Description ?? $"A {param.Type} parameter.")
|
||||
));
|
||||
}
|
||||
|
||||
// Agregar información de retorno si no es void
|
||||
if (!string.IsNullOrEmpty(method.ReturnType) && method.ReturnType != "void")
|
||||
{
|
||||
element.Add(new XElement("returns",
|
||||
method.ReturnDescription ?? $"A {method.ReturnType} value."
|
||||
));
|
||||
}
|
||||
|
||||
// Agregar ejemplos si hay
|
||||
foreach (var example in method.Examples)
|
||||
{
|
||||
element.Add(new XElement("example", example));
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
private XElement CreatePropertyElement(TypeDocumentation type, MemberDocumentation property)
|
||||
{
|
||||
var element = new XElement("member",
|
||||
new XAttribute("name", $"P:{type.FullName}.{property.Name}"),
|
||||
new XElement("summary", property.Description)
|
||||
);
|
||||
|
||||
if (!string.IsNullOrEmpty(property.ReturnType))
|
||||
{
|
||||
element.Add(new XElement("value",
|
||||
$"A {property.ReturnType} representing the property value."
|
||||
));
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
private XElement CreateEventElement(TypeDocumentation type, MemberDocumentation eventMember)
|
||||
{
|
||||
var element = new XElement("member",
|
||||
new XAttribute("name", $"E:{type.FullName}.{eventMember.Name}"),
|
||||
new XElement("summary", eventMember.Description)
|
||||
);
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
private XElement CreateFieldElement(TypeDocumentation type, MemberDocumentation field)
|
||||
{
|
||||
var element = new XElement("member",
|
||||
new XAttribute("name", $"F:{type.FullName}.{field.Name}"),
|
||||
new XElement("summary", field.Description)
|
||||
);
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
private string GenerateYamlOutput(DocumentationModel documentation)
|
||||
{
|
||||
// Implementación simplificada de YAML (mantenemos la existente)
|
||||
// Deserialize JSON primero, luego a YAML
|
||||
var json = JsonConvert.SerializeObject(documentation, Formatting.None,
|
||||
new JsonSerializerSettings
|
||||
{
|
||||
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
|
||||
NullValueHandling = NullValueHandling.Ignore
|
||||
});
|
||||
|
||||
// Convierte a YAML usando una implementación simple
|
||||
var yaml = new System.Text.StringBuilder();
|
||||
yaml.AppendLine("namespaces:");
|
||||
|
||||
var obj = JsonConvert.DeserializeObject<dynamic>(json);
|
||||
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: {EscapeYamlString(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");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -14,13 +14,14 @@ 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);
|
||||
|
||||
AppLogger.LogInfo($"Directorio de trabajo creado: {_workingDirectory}");
|
||||
}
|
||||
|
||||
public async Task<string> GenerateMetadataAsync(IEnumerable<AssemblyModel> assemblies)
|
||||
|
@ -29,17 +30,14 @@ namespace NetDocsForLLM.Services
|
|||
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}");
|
||||
AppLogger.LogInfo($"Directorio de metadatos creado: {metadataPath}");
|
||||
AppLogger.LogInfo($"Iniciando análisis en {DateTime.Now}");
|
||||
AppLogger.LogInfo($"Ensamblados a procesar: {assemblies.Count()}");
|
||||
|
||||
// Para cada ensamblado, generar documentación XML
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
await Task.Run(() => GenerateXmlDocumentation(assembly, metadataPath));
|
||||
}
|
||||
// Para cada ensamblado, generar documentación XML
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
await Task.Run(() => GenerateXmlDocumentation(assembly, metadataPath));
|
||||
}
|
||||
|
||||
return metadataPath;
|
||||
|
@ -52,11 +50,13 @@ namespace NetDocsForLLM.Services
|
|||
Assembly assembly = assemblyModel.LoadedAssembly;
|
||||
if (assembly == null)
|
||||
{
|
||||
_logWriter.WriteLine($"ERROR: El ensamblado {assemblyModel.Name} no está cargado");
|
||||
AppLogger.LogError($"ERROR: El ensamblado {assemblyModel.Name} no está cargado");
|
||||
return;
|
||||
}
|
||||
|
||||
_logWriter.WriteLine($"\n=== Procesando ensamblado: {assemblyModel.Name} ===");
|
||||
AppLogger.LogInfo($"\n=== Procesando ensamblado: {assemblyModel.Name} ===");
|
||||
AppLogger.LogInfo($"Ruta: {assemblyModel.FilePath}");
|
||||
AppLogger.LogInfo($"Versión: {assemblyModel.Version}");
|
||||
|
||||
// Crear el documento XML
|
||||
XDocument doc = new XDocument(
|
||||
|
@ -72,192 +72,427 @@ namespace NetDocsForLLM.Services
|
|||
// 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))
|
||||
// Cargar documentación XML existente si hay
|
||||
XDocument existingXmlDoc = null;
|
||||
if (assemblyModel.HasXmlDocumentation && File.Exists(assemblyModel.XmlDocPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
// Generar documentación para el tipo
|
||||
GenerateTypeDocumentation(type, membersElement);
|
||||
existingXmlDoc = XDocument.Load(assemblyModel.XmlDocPath);
|
||||
AppLogger.LogInfo($"Documentación XML cargada: {assemblyModel.XmlDocPath}");
|
||||
|
||||
// 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);
|
||||
}
|
||||
// Mostrar estadísticas del XML existente
|
||||
var memberNodes = existingXmlDoc.Descendants("member").ToList();
|
||||
AppLogger.LogInfo($"Entradas en XML existente: {memberNodes.Count}");
|
||||
|
||||
// Generar documentación para propiedades
|
||||
foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly))
|
||||
// Log de los primeros miembros para depuración
|
||||
foreach (var node in memberNodes.Take(5))
|
||||
{
|
||||
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);
|
||||
string id = node.Attribute("name")?.Value;
|
||||
AppLogger.LogDebug($" Miembro XML: {id}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logWriter.WriteLine($"Error procesando tipo {type.FullName}: {ex.Message}");
|
||||
AppLogger.LogWarning($"Error al cargar XML: {ex.Message}");
|
||||
existingXmlDoc = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AppLogger.LogWarning("No se encontró documentación XML para este ensamblado");
|
||||
}
|
||||
|
||||
// Procesar todos los tipos exportados
|
||||
var exportedTypes = assembly.GetExportedTypes().ToList();
|
||||
AppLogger.LogInfo($"Tipos exportados encontrados: {exportedTypes.Count}");
|
||||
|
||||
// Procesar todos los tipos exportados
|
||||
int typesWithMembers = 0;
|
||||
int typesWithoutMembers = 0;
|
||||
int totalMethods = 0;
|
||||
int totalProperties = 0;
|
||||
int totalEvents = 0;
|
||||
int totalFields = 0;
|
||||
|
||||
foreach (var type in exportedTypes)
|
||||
{
|
||||
try
|
||||
{
|
||||
AppLogger.IncrementTypeCount();
|
||||
AppLogger.LogDebug($"Procesando tipo: {type.FullName}");
|
||||
|
||||
// Generar documentación para el tipo
|
||||
bool hasMembers = GenerateTypeDocumentation(type, membersElement, existingXmlDoc);
|
||||
|
||||
if (hasMembers)
|
||||
{
|
||||
typesWithMembers++;
|
||||
}
|
||||
else
|
||||
{
|
||||
typesWithoutMembers++;
|
||||
AppLogger.LogWarning($"⚠️ No se encontraron miembros para el tipo: {type.FullName}");
|
||||
}
|
||||
|
||||
// Contar miembros por tipo para estadísticas
|
||||
var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)
|
||||
.Where(m => !m.IsSpecialName).ToList();
|
||||
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly).ToList();
|
||||
var events = type.GetEvents(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly).ToList();
|
||||
var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly).ToList();
|
||||
|
||||
totalMethods += methods.Count;
|
||||
totalProperties += properties.Count;
|
||||
totalEvents += events.Count;
|
||||
totalFields += fields.Count;
|
||||
|
||||
AppLogger.LogDebug($" Miembros del tipo {type.Name}: Métodos={methods.Count}, Propiedades={properties.Count}, Eventos={events.Count}, Campos={fields.Count}");
|
||||
|
||||
// Generar entradas XML para los miembros
|
||||
if (methods.Count > 0)
|
||||
{
|
||||
foreach (var method in methods)
|
||||
{
|
||||
AppLogger.IncrementMemberCount();
|
||||
GenerateMethodDocumentation(method, membersElement, existingXmlDoc);
|
||||
}
|
||||
}
|
||||
|
||||
if (properties.Count > 0)
|
||||
{
|
||||
foreach (var property in properties)
|
||||
{
|
||||
AppLogger.IncrementMemberCount();
|
||||
GeneratePropertyDocumentation(property, membersElement, existingXmlDoc);
|
||||
}
|
||||
}
|
||||
|
||||
if (events.Count > 0)
|
||||
{
|
||||
foreach (var eventInfo in events)
|
||||
{
|
||||
AppLogger.IncrementMemberCount();
|
||||
GenerateEventDocumentation(eventInfo, membersElement, existingXmlDoc);
|
||||
}
|
||||
}
|
||||
|
||||
if (fields.Count > 0)
|
||||
{
|
||||
foreach (var field in fields)
|
||||
{
|
||||
AppLogger.IncrementMemberCount();
|
||||
GenerateFieldDocumentation(field, membersElement, existingXmlDoc);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AppLogger.LogError($"Error procesando tipo {type.FullName}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// Guardar la documentación XML
|
||||
string xmlFileName = $"{assembly.GetName().Name}.xml";
|
||||
// Estadísticas finales
|
||||
AppLogger.LogInfo($"Estadísticas de procesamiento para {assemblyModel.Name}:");
|
||||
AppLogger.LogInfo($" Tipos totales: {exportedTypes.Count}");
|
||||
AppLogger.LogInfo($" Tipos con miembros: {typesWithMembers}");
|
||||
AppLogger.LogInfo($" Tipos sin miembros: {typesWithoutMembers}");
|
||||
AppLogger.LogInfo($" Total de métodos: {totalMethods}");
|
||||
AppLogger.LogInfo($" Total de propiedades: {totalProperties}");
|
||||
AppLogger.LogInfo($" Total de eventos: {totalEvents}");
|
||||
AppLogger.LogInfo($" Total de campos: {totalFields}");
|
||||
|
||||
// Guardar la documentación XML generada
|
||||
string xmlFileName = $"{assembly.GetName().Name}_generated.xml";
|
||||
string xmlFilePath = Path.Combine(outputPath, xmlFileName);
|
||||
|
||||
// Guardar con formato
|
||||
using (var writer = new XmlTextWriter(xmlFilePath, Encoding.UTF8))
|
||||
{
|
||||
writer.Formatting = Formatting.Indented;
|
||||
writer.Formatting = System.Xml.Formatting.Indented;
|
||||
writer.Indentation = 4;
|
||||
doc.Save(writer);
|
||||
}
|
||||
|
||||
_logWriter.WriteLine($"Documentación XML guardada en: {xmlFilePath}");
|
||||
AppLogger.LogInfo($"Documentación XML guardada en: {xmlFilePath}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logWriter.WriteLine($"ERROR general procesando ensamblado {assemblyModel.Name}: {ex.Message}");
|
||||
_logWriter.WriteLine(ex.StackTrace);
|
||||
AppLogger.LogError($"ERROR general procesando ensamblado {assemblyModel.Name}: {ex.Message}");
|
||||
AppLogger.LogError(ex.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateTypeDocumentation(Type type, XElement membersElement)
|
||||
private bool GenerateTypeDocumentation(Type type, XElement membersElement, XDocument existingXmlDoc)
|
||||
{
|
||||
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}"));
|
||||
// Intentar encontrar documentación existente
|
||||
string summary = null;
|
||||
string remarks = null;
|
||||
|
||||
// Si es una clase que hereda de otra (excepto Object), agregar esa información
|
||||
if (type.IsClass && type.BaseType != null && type.BaseType != typeof(object))
|
||||
if (existingXmlDoc != null)
|
||||
{
|
||||
memberElement.Add(new XElement("remarks",
|
||||
$"Inherits from {type.BaseType.Name}"));
|
||||
var existingMember = existingXmlDoc.Descendants("member")
|
||||
.FirstOrDefault(m => m.Attribute("name")?.Value == typeId);
|
||||
|
||||
if (existingMember != null)
|
||||
{
|
||||
AppLogger.LogDebug($" Encontrada documentación XML existente para {typeId}");
|
||||
summary = existingMember.Element("summary")?.Value?.Trim();
|
||||
remarks = existingMember.Element("remarks")?.Value?.Trim();
|
||||
}
|
||||
}
|
||||
|
||||
// Si implementa interfaces, mencionarlas
|
||||
var interfaces = type.GetInterfaces();
|
||||
if (interfaces.Length > 0)
|
||||
// Si no hay documentación existente, generar una descripción genérica
|
||||
if (string.IsNullOrEmpty(summary))
|
||||
{
|
||||
var implementsText = $"Implements: {string.Join(", ", interfaces.Select(i => i.Name))}";
|
||||
summary = $"Represents a {GetTypeKindDescription(type)}: {type.Name}";
|
||||
}
|
||||
|
||||
if (memberElement.Element("remarks") != null)
|
||||
var memberElement = new XElement("member", new XAttribute("name", typeId));
|
||||
memberElement.Add(new XElement("summary", summary));
|
||||
|
||||
// Agregar observaciones si están disponibles o generarlas
|
||||
if (!string.IsNullOrEmpty(remarks))
|
||||
{
|
||||
memberElement.Add(new XElement("remarks", remarks));
|
||||
}
|
||||
else
|
||||
{
|
||||
var remarksBuilder = new StringBuilder();
|
||||
|
||||
// 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.Element("remarks").Value += $"\n{implementsText}";
|
||||
remarksBuilder.AppendLine($"Inherits from {type.BaseType.Name}");
|
||||
}
|
||||
else
|
||||
|
||||
// Si implementa interfaces, mencionarlas
|
||||
var interfaces = type.GetInterfaces();
|
||||
if (interfaces.Length > 0)
|
||||
{
|
||||
memberElement.Add(new XElement("remarks", implementsText));
|
||||
if (remarksBuilder.Length > 0)
|
||||
remarksBuilder.AppendLine();
|
||||
|
||||
remarksBuilder.Append($"Implements: {string.Join(", ", interfaces.Select(i => i.Name))}");
|
||||
}
|
||||
|
||||
if (remarksBuilder.Length > 0)
|
||||
{
|
||||
memberElement.Add(new XElement("remarks", remarksBuilder.ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
membersElement.Add(memberElement);
|
||||
|
||||
// Verificar si el tipo tiene miembros
|
||||
bool hasMembers =
|
||||
type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly).Any(m => !m.IsSpecialName) ||
|
||||
type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly).Any() ||
|
||||
type.GetEvents(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly).Any() ||
|
||||
type.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly).Any();
|
||||
|
||||
return hasMembers;
|
||||
}
|
||||
|
||||
private void GenerateMethodDocumentation(MethodInfo method, XElement membersElement)
|
||||
private void GenerateMethodDocumentation(MethodInfo method, XElement membersElement, XDocument existingXmlDoc)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Generar ID XML para el método
|
||||
string methodId = GetMethodXmlId(method);
|
||||
var memberElement = new XElement("member", new XAttribute("name", methodId));
|
||||
AppLogger.LogDebug($" Procesando método: {methodId}");
|
||||
|
||||
// Agregar summary básico
|
||||
memberElement.Add(new XElement("summary",
|
||||
$"{method.Name} {GetMethodDescription(method)}"));
|
||||
// Intentar encontrar documentación existente
|
||||
string summary = null;
|
||||
string returns = null;
|
||||
Dictionary<string, string> paramDescriptions = new Dictionary<string, string>();
|
||||
|
||||
if (existingXmlDoc != null)
|
||||
{
|
||||
var existingMember = existingXmlDoc.Descendants("member")
|
||||
.FirstOrDefault(m => m.Attribute("name")?.Value == methodId);
|
||||
|
||||
if (existingMember != null)
|
||||
{
|
||||
summary = existingMember.Element("summary")?.Value?.Trim();
|
||||
returns = existingMember.Element("returns")?.Value?.Trim();
|
||||
|
||||
foreach (var paramElement in existingMember.Elements("param"))
|
||||
{
|
||||
string paramName = paramElement.Attribute("name")?.Value;
|
||||
string paramDesc = paramElement.Value?.Trim();
|
||||
|
||||
if (!string.IsNullOrEmpty(paramName) && !string.IsNullOrEmpty(paramDesc))
|
||||
{
|
||||
paramDescriptions[paramName] = paramDesc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Si no hay documentación existente, generar una descripción genérica
|
||||
if (string.IsNullOrEmpty(summary))
|
||||
{
|
||||
summary = $"{method.Name} {GetMethodDescription(method)}";
|
||||
}
|
||||
|
||||
var element = new XElement("member",
|
||||
new XAttribute("name", methodId),
|
||||
new XElement("summary", summary)
|
||||
);
|
||||
|
||||
// Agregar parámetros
|
||||
foreach (var param in method.GetParameters())
|
||||
var parameters = method.GetParameters();
|
||||
foreach (var param in parameters)
|
||||
{
|
||||
memberElement.Add(new XElement("param",
|
||||
string paramDesc;
|
||||
if (!paramDescriptions.TryGetValue(param.Name, out paramDesc))
|
||||
{
|
||||
paramDesc = GetParameterDescription(param);
|
||||
}
|
||||
|
||||
element.Add(new XElement("param",
|
||||
new XAttribute("name", param.Name),
|
||||
GetParameterDescription(param)));
|
||||
new XText(paramDesc)
|
||||
));
|
||||
}
|
||||
|
||||
// Agregar returns si no es void
|
||||
// Agregar información de retorno si no es void
|
||||
if (method.ReturnType != typeof(void))
|
||||
{
|
||||
memberElement.Add(new XElement("returns",
|
||||
$"A {GetSimpleTypeName(method.ReturnType)} value."));
|
||||
if (string.IsNullOrEmpty(returns))
|
||||
{
|
||||
returns = $"A {GetSimpleTypeName(method.ReturnType)} value.";
|
||||
}
|
||||
|
||||
element.Add(new XElement("returns", returns));
|
||||
}
|
||||
|
||||
membersElement.Add(memberElement);
|
||||
membersElement.Add(element);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logWriter.WriteLine($"Error generando documentación para método {method.Name}: {ex.Message}");
|
||||
AppLogger.LogError($"Error generando documentación para método {method.Name}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void GeneratePropertyDocumentation(PropertyInfo property, XElement membersElement)
|
||||
private void GeneratePropertyDocumentation(PropertyInfo property, XElement membersElement, XDocument existingXmlDoc)
|
||||
{
|
||||
try
|
||||
{
|
||||
string propertyId = $"P:{property.DeclaringType.FullName}.{property.Name}";
|
||||
var memberElement = new XElement("member", new XAttribute("name", propertyId));
|
||||
AppLogger.LogDebug($" Procesando propiedad: {propertyId}");
|
||||
|
||||
// Accesibilidad (get/set)
|
||||
string access = "";
|
||||
if (property.CanRead) access += "get; ";
|
||||
if (property.CanWrite) access += "set; ";
|
||||
// Intentar encontrar documentación existente
|
||||
string summary = null;
|
||||
string value = null;
|
||||
|
||||
// Agregar summary
|
||||
memberElement.Add(new XElement("summary",
|
||||
$"Gets{(property.CanWrite ? " or sets" : "")} the {property.Name} property."));
|
||||
if (existingXmlDoc != null)
|
||||
{
|
||||
var existingMember = existingXmlDoc.Descendants("member")
|
||||
.FirstOrDefault(m => m.Attribute("name")?.Value == propertyId);
|
||||
|
||||
// Agregar valor
|
||||
memberElement.Add(new XElement("value",
|
||||
$"A {GetSimpleTypeName(property.PropertyType)} representing the {property.Name}."));
|
||||
if (existingMember != null)
|
||||
{
|
||||
summary = existingMember.Element("summary")?.Value?.Trim();
|
||||
value = existingMember.Element("value")?.Value?.Trim();
|
||||
}
|
||||
}
|
||||
|
||||
membersElement.Add(memberElement);
|
||||
// Si no hay documentación existente, generar una descripción genérica
|
||||
if (string.IsNullOrEmpty(summary))
|
||||
{
|
||||
summary = $"Gets{(property.CanWrite ? " or sets" : "")} the {property.Name} property.";
|
||||
}
|
||||
|
||||
var element = new XElement("member",
|
||||
new XAttribute("name", propertyId),
|
||||
new XElement("summary", summary)
|
||||
);
|
||||
|
||||
// Agregar descripción de valor si está disponible o generarla
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
value = $"A {GetSimpleTypeName(property.PropertyType)} representing the {property.Name}.";
|
||||
}
|
||||
|
||||
element.Add(new XElement("value", value));
|
||||
|
||||
membersElement.Add(element);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logWriter.WriteLine($"Error generando documentación para propiedad {property.Name}: {ex.Message}");
|
||||
AppLogger.LogError($"Error generando documentación para propiedad {property.Name}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateEventDocumentation(EventInfo eventInfo, XElement membersElement)
|
||||
private void GenerateEventDocumentation(EventInfo eventInfo, XElement membersElement, XDocument existingXmlDoc)
|
||||
{
|
||||
try
|
||||
{
|
||||
string eventId = $"E:{eventInfo.DeclaringType.FullName}.{eventInfo.Name}";
|
||||
var memberElement = new XElement("member", new XAttribute("name", eventId));
|
||||
AppLogger.LogDebug($" Procesando evento: {eventId}");
|
||||
|
||||
// Agregar summary
|
||||
memberElement.Add(new XElement("summary",
|
||||
$"Occurs when {eventInfo.Name} is raised."));
|
||||
// Intentar encontrar documentación existente
|
||||
string summary = null;
|
||||
|
||||
if (existingXmlDoc != null)
|
||||
{
|
||||
var existingMember = existingXmlDoc.Descendants("member")
|
||||
.FirstOrDefault(m => m.Attribute("name")?.Value == eventId);
|
||||
|
||||
if (existingMember != null)
|
||||
{
|
||||
summary = existingMember.Element("summary")?.Value?.Trim();
|
||||
}
|
||||
}
|
||||
|
||||
// Si no hay documentación existente, generar una descripción genérica
|
||||
if (string.IsNullOrEmpty(summary))
|
||||
{
|
||||
summary = $"Occurs when {eventInfo.Name} is raised.";
|
||||
}
|
||||
|
||||
var element = new XElement("member",
|
||||
new XAttribute("name", eventId),
|
||||
new XElement("summary", summary)
|
||||
);
|
||||
|
||||
// Agregar remarks con el tipo del handler
|
||||
memberElement.Add(new XElement("remarks",
|
||||
$"Event handler type: {GetSimpleTypeName(eventInfo.EventHandlerType)}"));
|
||||
element.Add(new XElement("remarks",
|
||||
$"Event handler type: {GetSimpleTypeName(eventInfo.EventHandlerType)}"
|
||||
));
|
||||
|
||||
membersElement.Add(memberElement);
|
||||
membersElement.Add(element);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logWriter.WriteLine($"Error generando documentación para evento {eventInfo.Name}: {ex.Message}");
|
||||
AppLogger.LogError($"Error generando documentación para evento {eventInfo.Name}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateFieldDocumentation(FieldInfo field, XElement membersElement)
|
||||
private void GenerateFieldDocumentation(FieldInfo field, XElement membersElement, XDocument existingXmlDoc)
|
||||
{
|
||||
try
|
||||
{
|
||||
string fieldId = $"F:{field.DeclaringType.FullName}.{field.Name}";
|
||||
var memberElement = new XElement("member", new XAttribute("name", fieldId));
|
||||
AppLogger.LogDebug($" Procesando campo: {fieldId}");
|
||||
|
||||
// Intentar encontrar documentación existente
|
||||
string summary = null;
|
||||
|
||||
if (existingXmlDoc != null)
|
||||
{
|
||||
var existingMember = existingXmlDoc.Descendants("member")
|
||||
.FirstOrDefault(m => m.Attribute("name")?.Value == fieldId);
|
||||
|
||||
if (existingMember != null)
|
||||
{
|
||||
summary = existingMember.Element("summary")?.Value?.Trim();
|
||||
}
|
||||
}
|
||||
|
||||
// Modificadores
|
||||
string modifiers = "";
|
||||
|
@ -265,15 +500,22 @@ namespace NetDocsForLLM.Services
|
|||
if (field.IsInitOnly) modifiers += "readonly ";
|
||||
if (field.IsLiteral) modifiers += "constant ";
|
||||
|
||||
// Agregar summary
|
||||
memberElement.Add(new XElement("summary",
|
||||
$"Represents the {modifiers}{field.Name} field."));
|
||||
// Si no hay documentación existente, generar una descripción genérica
|
||||
if (string.IsNullOrEmpty(summary))
|
||||
{
|
||||
summary = $"Represents the {modifiers}{field.Name} field.";
|
||||
}
|
||||
|
||||
membersElement.Add(memberElement);
|
||||
var element = new XElement("member",
|
||||
new XAttribute("name", fieldId),
|
||||
new XElement("summary", summary)
|
||||
);
|
||||
|
||||
membersElement.Add(element);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logWriter.WriteLine($"Error generando documentación para campo {field.Name}: {ex.Message}");
|
||||
AppLogger.LogError($"Error generando documentación para campo {field.Name}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -281,44 +523,64 @@ namespace NetDocsForLLM.Services
|
|||
|
||||
private string GetMethodXmlId(MethodInfo method)
|
||||
{
|
||||
var parameters = method.GetParameters();
|
||||
if (parameters.Length == 0)
|
||||
try
|
||||
{
|
||||
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})";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AppLogger.LogError($"Error generando XML ID para método {method.Name}: {ex.Message}");
|
||||
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)
|
||||
try
|
||||
{
|
||||
return $"{GetParameterTypeName(type.GetElementType())}[]";
|
||||
}
|
||||
// El formato de los parámetros en los IDs XML de documentación es específico
|
||||
|
||||
// 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<>))
|
||||
// Arrays
|
||||
if (type.IsArray)
|
||||
{
|
||||
return $"System.Collections.Generic.List{{{genericArgs}}}";
|
||||
return $"{GetParameterTypeName(type.GetElementType())}[]";
|
||||
}
|
||||
|
||||
// Para otros tipos genéricos, usar el nombre completo con {}
|
||||
return $"{genericTypeDef.FullName.Split('`')[0]}{{{genericArgs}}}";
|
||||
}
|
||||
// Genéricos
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
var genericTypeDef = type.GetGenericTypeDefinition();
|
||||
var genericArgs = string.Join(",", type.GetGenericArguments().Select(GetParameterTypeName));
|
||||
|
||||
// Para tipos normales, usar el nombre completo
|
||||
return type.FullName;
|
||||
// Para tipos como List<T>, devolver algo como System.Collections.Generic.List{T}
|
||||
string baseTypeName;
|
||||
if (genericTypeDef.FullName != null && genericTypeDef.FullName.Contains('`'))
|
||||
{
|
||||
baseTypeName = genericTypeDef.FullName.Split('`')[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
baseTypeName = genericTypeDef.Namespace + "." + genericTypeDef.Name.Split('`')[0];
|
||||
}
|
||||
|
||||
return $"{baseTypeName}{{{genericArgs}}}";
|
||||
}
|
||||
|
||||
// Para tipos normales, usar el nombre completo
|
||||
return type.FullName;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// En caso de error, devolver un nombre genérico
|
||||
return "System.Object";
|
||||
}
|
||||
}
|
||||
|
||||
private string GetTypeKindDescription(Type type)
|
||||
|
@ -346,7 +608,7 @@ namespace NetDocsForLLM.Services
|
|||
{
|
||||
return "performs an operation.";
|
||||
}
|
||||
return $"gets a {GetSimpleTypeName(method.ReturnType)} value.";
|
||||
return $"returns a {GetSimpleTypeName(method.ReturnType)} value.";
|
||||
}
|
||||
|
||||
return $"with {parameters.Length} parameter{(parameters.Length > 1 ? "s" : "")}.";
|
||||
|
@ -383,49 +645,57 @@ namespace NetDocsForLLM.Services
|
|||
|
||||
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)
|
||||
try
|
||||
{
|
||||
return GetSimpleTypeName(type.GetElementType());
|
||||
}
|
||||
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(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)
|
||||
// Si es un tipo por referencia (como out o ref parámetros)
|
||||
if (type.IsByRef)
|
||||
{
|
||||
baseName = baseName.Substring(0, tickIndex);
|
||||
return GetSimpleTypeName(type.GetElementType());
|
||||
}
|
||||
|
||||
var argTypes = type.GetGenericArguments()
|
||||
.Select(GetSimpleTypeName)
|
||||
.ToArray();
|
||||
// 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";
|
||||
|
||||
return $"{baseName}<{string.Join(", ", argTypes)}>";
|
||||
// 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;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// En caso de error, devolver un nombre genérico
|
||||
return "object";
|
||||
}
|
||||
|
||||
// Para tipos normales, usar solo el nombre simple
|
||||
return type.Name;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
//--- LogViewModel.cs ---
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using NetDocsForLLM.Services;
|
||||
using NetDocsForLLM.ViewModels;
|
||||
using Ookii.Dialogs.Wpf;
|
||||
using System;
|
||||
using System.Drawing.Printing;
|
||||
using System.IO;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Forms;
|
||||
using System.Windows.Media.Media3D;
|
||||
using System.Windows;
|
||||
using System.Windows.Threading;
|
||||
using static System.Net.Mime.MediaTypeNames;
|
||||
using static System.Windows.Forms.VisualStyles.VisualStyleElement.TrayNotify;
|
||||
|
||||
namespace NetDocsForLLM.ViewModels
|
||||
{
|
||||
public class LogViewModel : ObservableObject
|
||||
{
|
||||
private string _logContent;
|
||||
private int _logLevel;
|
||||
private readonly DispatcherTimer _refreshTimer;
|
||||
|
||||
public string LogContent
|
||||
{
|
||||
get => _logContent;
|
||||
set => SetProperty(ref _logContent, value);
|
||||
}
|
||||
|
||||
public int LogLevel
|
||||
{
|
||||
get => _logLevel;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _logLevel, value))
|
||||
{
|
||||
UpdateLogContent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int ErrorCount => AppLogger.ErrorCount;
|
||||
public int WarningCount => AppLogger.WarningCount;
|
||||
public int TypeCount => AppLogger.TypeCount;
|
||||
public int MemberCount => AppLogger.MemberCount;
|
||||
|
||||
public IRelayCommand ClearLogCommand { get; }
|
||||
public IRelayCommand SaveLogCommand { get; }
|
||||
public IRelayCommand RefreshLogCommand { get; }
|
||||
|
||||
public LogViewModel()
|
||||
{
|
||||
_logLevel = LogLevel; // Por defecto, mostrar INFO y errores
|
||||
_logContent = "";
|
||||
|
||||
ClearLogCommand = new RelayCommand(ClearLog);
|
||||
SaveLogCommand = new RelayCommand(SaveLog);
|
||||
RefreshLogCommand = new RelayCommand(UpdateLogContent);
|
||||
|
||||
// Crear timer para actualizar el contenido
|
||||
_refreshTimer = new DispatcherTimer
|
||||
{
|
||||
Interval = TimeSpan.FromSeconds(1)
|
||||
};
|
||||
|
||||
_refreshTimer.Tick += (s, e) => UpdateProperties();
|
||||
_refreshTimer.Start();
|
||||
|
||||
// Suscribirse al evento de actualización de logs
|
||||
AppLogger.LogUpdated += OnLogUpdated;
|
||||
|
||||
UpdateLogContent();
|
||||
}
|
||||
|
||||
private void OnLogUpdated(object sender, EventArgs e)
|
||||
{
|
||||
// Este método podría ser llamado desde otro hilo, así que usamos el Dispatcher
|
||||
App.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
UpdateLogContent();
|
||||
UpdateProperties();
|
||||
});
|
||||
}
|
||||
|
||||
private void UpdateProperties()
|
||||
{
|
||||
OnPropertyChanged(nameof(ErrorCount));
|
||||
OnPropertyChanged(nameof(WarningCount));
|
||||
OnPropertyChanged(nameof(TypeCount));
|
||||
OnPropertyChanged(nameof(MemberCount));
|
||||
}
|
||||
|
||||
private void UpdateLogContent()
|
||||
{
|
||||
LogContent = AppLogger.GetContent((LogLevel)_logLevel);
|
||||
}
|
||||
|
||||
private void ClearLog()
|
||||
{
|
||||
AppLogger.Clear();
|
||||
UpdateLogContent();
|
||||
}
|
||||
|
||||
private void SaveLog()
|
||||
{
|
||||
var dialog = new VistaSaveFileDialog
|
||||
{
|
||||
Filter = "Archivos de texto (*.txt)|*.txt|Archivos de log (*.log)|*.log",
|
||||
DefaultExt = ".txt",
|
||||
Title = "Guardar archivo de log"
|
||||
};
|
||||
|
||||
if (dialog.ShowDialog() == true)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.WriteAllText(dialog.FileName, AppLogger.GetContent());
|
||||
AppLogger.LogInfo($"Log guardado en: {dialog.FileName}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AppLogger.LogError($"Error al guardar el log: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,11 +12,11 @@ using System.Windows;
|
|||
|
||||
namespace NetDocsForLLM.ViewModels
|
||||
{
|
||||
public class MainViewModel : ObservableObject
|
||||
public partial class MainViewModel : ObservableObject
|
||||
{
|
||||
private readonly IAssemblyAnalyzer _assemblyAnalyzer;
|
||||
private readonly IDocumentationGenerator _documentationGenerator;
|
||||
|
||||
|
||||
private ObservableCollection<AssemblyModel> _selectedAssemblies;
|
||||
private DocumentationModel _documentationModel;
|
||||
private ExportSettings _settings;
|
||||
|
@ -24,6 +24,8 @@ namespace NetDocsForLLM.ViewModels
|
|||
private string _documentationStats;
|
||||
private string _statusMessage;
|
||||
private bool _isProcessing;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _hasGeneratedDocumentation;
|
||||
|
||||
public ObservableCollection<AssemblyModel> SelectedAssemblies
|
||||
|
@ -68,20 +70,25 @@ namespace NetDocsForLLM.ViewModels
|
|||
set => SetProperty(ref _isProcessing, value);
|
||||
}
|
||||
|
||||
public bool HasGeneratedDocumentation
|
||||
{
|
||||
get => _hasGeneratedDocumentation;
|
||||
set => SetProperty(ref _hasGeneratedDocumentation, value);
|
||||
}
|
||||
|
||||
public bool HasSelectedAssemblies => SelectedAssemblies.Count > 0;
|
||||
|
||||
// Comandos
|
||||
public IRelayCommand SelectAssembliesCommand { get; }
|
||||
public IRelayCommand<AssemblyModel> RemoveAssemblyCommand { get; }
|
||||
public IRelayCommand GenerateDocumentationCommand { get; }
|
||||
public IRelayCommand CopyToClipboardCommand { get; }
|
||||
public IRelayCommand ExportDocumentationCommand { get; }
|
||||
|
||||
// Estos campos se actualizan desde propiedades computadas
|
||||
private IRelayCommand _copyToClipboardCommand;
|
||||
private IRelayCommand _exportDocumentationCommand;
|
||||
|
||||
// Redefine los comandos con canExecute actualizado
|
||||
public IRelayCommand CopyToClipboardCommand =>
|
||||
_copyToClipboardCommand ??= new RelayCommand(CopyToClipboard, () => HasGeneratedDocumentation);
|
||||
|
||||
public IRelayCommand ExportDocumentationCommand =>
|
||||
_exportDocumentationCommand ??= new RelayCommand(ExportDocumentation, () => HasGeneratedDocumentation);
|
||||
|
||||
// Constructor
|
||||
public MainViewModel(IAssemblyAnalyzer assemblyAnalyzer, IDocumentationGenerator documentationGenerator)
|
||||
{
|
||||
_assemblyAnalyzer = assemblyAnalyzer ?? throw new ArgumentNullException(nameof(assemblyAnalyzer));
|
||||
|
@ -99,16 +106,27 @@ namespace NetDocsForLLM.ViewModels
|
|||
SelectAssembliesCommand = new RelayCommand(SelectAssemblies);
|
||||
RemoveAssemblyCommand = new RelayCommand<AssemblyModel>(RemoveAssembly);
|
||||
GenerateDocumentationCommand = new AsyncRelayCommand(GenerateDocumentationAsync);
|
||||
CopyToClipboardCommand = new RelayCommand(CopyToClipboard, () => HasGeneratedDocumentation);
|
||||
ExportDocumentationCommand = new RelayCommand(ExportDocumentation, () => HasGeneratedDocumentation);
|
||||
|
||||
// Subscribe to collection changes to update HasSelectedAssemblies
|
||||
_selectedAssemblies.CollectionChanged += (sender, e) =>
|
||||
_selectedAssemblies.CollectionChanged += (sender, e) =>
|
||||
{
|
||||
OnPropertyChanged(nameof(HasSelectedAssemblies));
|
||||
};
|
||||
}
|
||||
|
||||
// Asegúrate de que UpdateCanExecuteForCommands se llame cuando cambie HasGeneratedDocumentation
|
||||
partial void OnHasGeneratedDocumentationChanged(bool value)
|
||||
{
|
||||
UpdateCanExecuteForCommands();
|
||||
}
|
||||
|
||||
// Método para actualizar el CanExecute de los comandos
|
||||
private void UpdateCanExecuteForCommands()
|
||||
{
|
||||
(CopyToClipboardCommand as RelayCommand)?.NotifyCanExecuteChanged();
|
||||
(ExportDocumentationCommand as RelayCommand)?.NotifyCanExecuteChanged();
|
||||
}
|
||||
|
||||
private void SelectAssemblies()
|
||||
{
|
||||
var dialog = new VistaOpenFileDialog
|
||||
|
@ -156,7 +174,7 @@ namespace NetDocsForLLM.ViewModels
|
|||
{
|
||||
if (SelectedAssemblies.Count == 0)
|
||||
{
|
||||
MessageBox.Show("Por favor, seleccione al menos una librería para generar documentación.",
|
||||
MessageBox.Show("Por favor, seleccione al menos una librería para generar documentación.",
|
||||
"Sin librerías", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
@ -167,27 +185,31 @@ namespace NetDocsForLLM.ViewModels
|
|||
StatusMessage = "Generando documentación...";
|
||||
|
||||
// Generate documentation
|
||||
DocumentationModel = await Task.Run(() =>
|
||||
DocumentationModel = await Task.Run(() =>
|
||||
_documentationGenerator.GenerateDocumentation(SelectedAssemblies, Settings));
|
||||
|
||||
// Generate preview
|
||||
DocumentationPreview = await Task.Run(() =>
|
||||
DocumentationPreview = await Task.Run(() =>
|
||||
_documentationGenerator.GenerateDocumentationPreview(DocumentationModel, Settings));
|
||||
|
||||
// Update stats
|
||||
var totalTypes = DocumentationModel.Namespaces.Sum(n => n.Types.Count);
|
||||
var totalMembers = DocumentationModel.Namespaces.Sum(n =>
|
||||
var totalMembers = DocumentationModel.Namespaces.Sum(n =>
|
||||
n.Types.Sum(t => t.Members.Count));
|
||||
|
||||
|
||||
DocumentationStats = $"Resumen: {DocumentationModel.Namespaces.Count} namespaces, " +
|
||||
$"{totalTypes} tipos, {totalMembers} miembros";
|
||||
|
||||
// Estas dos líneas son cruciales - aseguran que HasGeneratedDocumentation se actualice
|
||||
// y que los comandos se habiliten
|
||||
HasGeneratedDocumentation = true;
|
||||
UpdateCanExecuteForCommands();
|
||||
|
||||
StatusMessage = "Documentación generada correctamente";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"Error al generar la documentación: {ex.Message}",
|
||||
MessageBox.Show($"Error al generar la documentación: {ex.Message}",
|
||||
"Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
StatusMessage = "Error al generar documentación";
|
||||
}
|
||||
|
@ -209,7 +231,7 @@ namespace NetDocsForLLM.ViewModels
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"Error al copiar al portapapeles: {ex.Message}",
|
||||
MessageBox.Show($"Error al copiar al portapapeles: {ex.Message}",
|
||||
"Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
@ -219,13 +241,33 @@ namespace NetDocsForLLM.ViewModels
|
|||
if (string.IsNullOrEmpty(DocumentationPreview))
|
||||
return;
|
||||
|
||||
// Determinar extensión y filtro basado en formato seleccionado
|
||||
string extension;
|
||||
string filter;
|
||||
|
||||
switch (Settings.OutputFormat)
|
||||
{
|
||||
case OutputFormat.Xml:
|
||||
extension = ".xml";
|
||||
filter = "Archivos XML (*.xml)|*.xml";
|
||||
break;
|
||||
case OutputFormat.Json:
|
||||
extension = ".json";
|
||||
filter = "Archivos JSON (*.json)|*.json";
|
||||
break;
|
||||
case OutputFormat.Yaml:
|
||||
default:
|
||||
extension = ".yaml";
|
||||
filter = "Archivos YAML (*.yaml;*.yml)|*.yaml;*.yml";
|
||||
break;
|
||||
}
|
||||
|
||||
var dialog = new VistaSaveFileDialog
|
||||
{
|
||||
Filter = Settings.OutputFormat == OutputFormat.Json
|
||||
? "Archivos JSON (*.json)|*.json"
|
||||
: "Archivos YAML (*.yaml;*.yml)|*.yaml;*.yml",
|
||||
DefaultExt = Settings.OutputFormat == OutputFormat.Json ? ".json" : ".yaml",
|
||||
Title = "Guardar documentación"
|
||||
Filter = filter,
|
||||
DefaultExt = extension,
|
||||
Title = "Guardar documentación",
|
||||
OverwritePrompt = true
|
||||
};
|
||||
|
||||
if (dialog.ShowDialog() == true)
|
||||
|
@ -237,10 +279,10 @@ namespace NetDocsForLLM.ViewModels
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"Error al guardar el archivo: {ex.Message}",
|
||||
MessageBox.Show($"Error al guardar el archivo: {ex.Message}",
|
||||
"Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
<UserControl x:Class="NetDocsForLLM.Views.LogView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:NetDocsForLLM.Views"
|
||||
mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Grid Margin="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
|
||||
<Button Content="Limpiar Log" Command="{Binding ClearLogCommand}" Padding="10,5" Margin="0,0,10,0" />
|
||||
|
||||
<Button Content="Guardar Log" Command="{Binding SaveLogCommand}" Padding="10,5" Margin="0,0,10,0" />
|
||||
|
||||
<Button Content="Actualizar" Command="{Binding RefreshLogCommand}" Padding="10,5" Margin="0,0,10,0" />
|
||||
|
||||
<TextBlock VerticalAlignment="Center" Text="Nivel de detalle:" />
|
||||
<ComboBox Margin="5,0,0,0" SelectedIndex="{Binding LogLevel}" Width="120">
|
||||
<ComboBoxItem Content="Errores" />
|
||||
<ComboBoxItem Content="Advertencias" />
|
||||
<ComboBoxItem Content="Información" />
|
||||
<ComboBoxItem Content="Depuración" />
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
|
||||
<TextBox Grid.Row="1" Text="{Binding LogContent, Mode=OneWay}" IsReadOnly="True"
|
||||
VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" FontFamily="Consolas"
|
||||
FontSize="12" AcceptsReturn="True" TextWrapping="NoWrap" Background="#f5f5f5" Padding="10" />
|
||||
|
||||
<StackPanel Grid.Row="2" Margin="0,10,0,0" Orientation="Horizontal">
|
||||
<TextBlock Text="Errores:" Foreground="Red" FontWeight="Bold" Margin="0,0,5,0" />
|
||||
<TextBlock Text="{Binding ErrorCount}" />
|
||||
|
||||
<TextBlock Text="Advertencias:" Foreground="Orange" FontWeight="Bold" Margin="15,0,5,0" />
|
||||
<TextBlock Text="{Binding WarningCount}" />
|
||||
|
||||
<TextBlock Text="Total tipos analizados:" FontWeight="Bold" Margin="15,0,5,0" />
|
||||
<TextBlock Text="{Binding TypeCount}" />
|
||||
|
||||
<TextBlock Text="Total miembros analizados:" FontWeight="Bold" Margin="15,0,5,0" />
|
||||
<TextBlock Text="{Binding MemberCount}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
|
@ -0,0 +1,15 @@
|
|||
//--- LogView.xaml.cs ---
|
||||
using NetDocsForLLM.ViewModels;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace NetDocsForLLM.Views
|
||||
{
|
||||
public partial class LogView : UserControl
|
||||
{
|
||||
public LogView()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = new LogViewModel();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue