diff --git a/App.xaml b/App.xaml
new file mode 100644
index 0000000..3949fa7
--- /dev/null
+++ b/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/App.xaml.cs b/App.xaml.cs
new file mode 100644
index 0000000..ea9a93d
--- /dev/null
+++ b/App.xaml.cs
@@ -0,0 +1,13 @@
+using System.Configuration;
+using System.Data;
+using System.Windows;
+
+namespace CodeMergerNG;
+
+///
+/// Interaction logic for App.xaml
+///
+public partial class App : Application
+{
+}
+
diff --git a/AssemblyInfo.cs b/AssemblyInfo.cs
new file mode 100644
index 0000000..cc29e7f
--- /dev/null
+++ b/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly:ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/CodeMerger.cs b/CodeMerger.cs
new file mode 100644
index 0000000..19e08ed
--- /dev/null
+++ b/CodeMerger.cs
@@ -0,0 +1,1353 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+///
+/// Clase para realizar análisis y merge de código C# utilizando Roslyn
+///
+public class cCodeMerger
+{
+ #region Constantes
+
+ // Constantes para namespace y clase virtual
+ private const string VIRTUAL_NAMESPACE = "__VirtualNamespace__";
+ private const string VIRTUAL_CLASS = "__VirtualClass__";
+
+ #endregion
+
+ #region Propiedades
+
+ // Configuración para namespace y clase por defecto
+ public string DefaultNamespace { get; set; } = VIRTUAL_NAMESPACE;
+ public string DefaultClass { get; set; } = VIRTUAL_CLASS;
+
+ // Log de operaciones
+ private StringBuilder _log = new StringBuilder();
+
+ // Diccionarios para búsqueda rápida
+ private Dictionary _methodsBySignature = new Dictionary();
+ private Dictionary _propertiesByName = new Dictionary();
+ private Dictionary _fieldsByName = new Dictionary();
+ private Dictionary _classesByName = new Dictionary();
+ private Dictionary _namespacesByName = new Dictionary();
+ private Dictionary _usingsByName = new Dictionary();
+
+ #endregion
+
+ #region Métodos Públicos
+
+ ///
+ /// Constructor de la clase CodeMerger
+ ///
+ /// Namespace por defecto a usar cuando no se especifique
+ /// Clase por defecto a usar cuando no se especifique
+ public cCodeMerger(string defaultNamespace = null, string defaultClass = null)
+ {
+ if (!string.IsNullOrEmpty(defaultNamespace))
+ DefaultNamespace = defaultNamespace;
+
+ if (!string.IsNullOrEmpty(defaultClass))
+ DefaultClass = defaultClass;
+
+ LogInfo("CodeMerger initialized");
+ }
+
+ ///
+ /// Verifica rápidamente si un código contiene un namespace específico
+ ///
+ public bool ContainsNamespace(string code, string namespaceName)
+ {
+ if (string.IsNullOrEmpty(code) || string.IsNullOrEmpty(namespaceName))
+ return false;
+
+ string pattern = $@"namespace\s+{Regex.Escape(namespaceName)}\s*{{";
+ return Regex.IsMatch(code, pattern);
+ }
+
+ ///
+ /// Verifica rápidamente si un código contiene una clase específica
+ ///
+ public bool ContainsClass(string code, string className)
+ {
+ if (string.IsNullOrEmpty(code) || string.IsNullOrEmpty(className))
+ return false;
+
+ string pattern = $@"class\s+{Regex.Escape(className)}\s*{{";
+ return Regex.IsMatch(code, pattern);
+ }
+
+ ///
+ /// Analiza el código C# y genera un árbol de CodeElement
+ ///
+ /// Código C# a analizar
+ /// Elemento raíz del árbol generado
+ public CodeElement AnalyzeCode(string code)
+ {
+ if (string.IsNullOrEmpty(code))
+ {
+ LogError("Empty code provided for analysis");
+ return null;
+ }
+
+ try
+ {
+ LogInfo("Starting code analysis");
+
+ // Preparar código asegurando que tenga namespace y clase si es necesario
+ string preparedCode = PrepareCodeForParsing(code);
+
+ // Parsear código con Roslyn
+ SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(preparedCode);
+ CompilationUnitSyntax root = syntaxTree.GetCompilationUnitRoot();
+
+ // Construir árbol de CodeElement
+ CodeElement rootElement = BuildCodeElementTree(root);
+
+ // Construir índices para búsqueda rápida
+ BuildSearchIndices(rootElement);
+
+ LogInfo($"Code analysis completed. Root element: {rootElement.Type} {rootElement.Name}");
+
+ return rootElement;
+ }
+ catch (Exception ex)
+ {
+ LogError($"Error analyzing code: {ex.Message}");
+ return null;
+ }
+ }
+
+ ///
+ /// Realiza el merge de código snippet dentro de código master
+ ///
+ /// Código principal que recibirá las modificaciones
+ /// Código a integrar en el master
+ /// Código resultante del merge
+ public string MergeCode(string masterCode, string snippetCode)
+ {
+ try
+ {
+ LogInfo("Starting code merge operation");
+
+ // Analizar ambos códigos
+ CodeElement masterRoot = AnalyzeCode(masterCode);
+ CodeElement snippetRoot = AnalyzeCode(snippetCode);
+
+ if (masterRoot == null || snippetRoot == null)
+ {
+ LogError("Could not analyze one or both code inputs");
+ return masterCode;
+ }
+
+ // Realizar el merge
+ CodeElement mergedRoot = MergeElements(masterRoot, snippetRoot);
+
+ // Reconstruir el código resultante
+ string result = ReconstructCode(mergedRoot);
+
+ LogInfo("Code merge completed successfully");
+
+ return result;
+ }
+ catch (Exception ex)
+ {
+ LogError($"Error during code merge: {ex.Message}");
+ return masterCode; // En caso de error, devolvemos el código master sin cambios
+ }
+ }
+
+ ///
+ /// Obtiene el log de operaciones realizadas
+ ///
+ public string GetLog()
+ {
+ return _log.ToString();
+ }
+
+ ///
+ /// Limpia el log de operaciones
+ ///
+ public void ClearLog()
+ {
+ _log.Clear();
+ LogInfo("Log cleared");
+ }
+
+ #endregion
+
+ #region Métodos Privados para Análisis de Código
+
+ ///
+ /// Prepara el código para análisis, añadiendo namespace y clase si es necesario
+ ///
+ private string PrepareCodeForParsing(string code)
+ {
+ bool hasNamespace = ContainsNamespace(code, ".*?");
+ bool hasClass = ContainsClass(code, ".*?");
+
+ if (hasNamespace && hasClass)
+ return code;
+
+ if (!hasNamespace && !hasClass)
+ {
+ // Ni namespace ni clase: envolver en ambos
+ LogInfo($"Adding virtual namespace '{DefaultNamespace}' and class '{DefaultClass}'");
+ return $"namespace {DefaultNamespace} {{ class {DefaultClass} {{ {code} }} }}";
+ }
+ else if (hasNamespace && !hasClass)
+ {
+ // Tiene namespace pero no clase
+ LogInfo($"Adding virtual class '{DefaultClass}'");
+ // Necesitamos insertar la clase dentro del namespace existente
+ Match namespaceMatch = Regex.Match(code, @"namespace\s+([^\s{]+)\s*{");
+ if (namespaceMatch.Success)
+ {
+ int openBracePos = code.IndexOf('{', namespaceMatch.Index);
+ if (openBracePos >= 0)
+ {
+ return code.Substring(0, openBracePos + 1) +
+ $" class {DefaultClass} {{ " +
+ code.Substring(openBracePos + 1) +
+ " } ";
+ }
+ }
+ return $"namespace {DefaultNamespace} {{ class {DefaultClass} {{ {code} }} }}";
+ }
+ else // hasClass && !hasNamespace
+ {
+ // Tiene clase pero no namespace
+ LogInfo($"Adding virtual namespace '{DefaultNamespace}'");
+ return $"namespace {DefaultNamespace} {{ {code} }}";
+ }
+ }
+
+ ///
+ /// Construye el árbol de CodeElement a partir del árbol sintáctico de Roslyn
+ ///
+ private CodeElement BuildCodeElementTree(CompilationUnitSyntax compilationUnit)
+ {
+ // Crear elemento raíz para representar el archivo completo
+ var rootElement = new CodeElement
+ {
+ Type = CodeElement.ElementType.Other,
+ Name = "Root",
+ OriginalText = compilationUnit.ToString(),
+ StartPosition = 0,
+ Length = compilationUnit.ToString().Length
+ };
+
+ // Procesar todas las directivas using
+ foreach (var usingDirective in compilationUnit.Usings)
+ {
+ var usingElement = new CodeElement
+ {
+ Type = CodeElement.ElementType.Using,
+ Name = usingDirective.Name.ToString(),
+ OriginalText = usingDirective.ToString(),
+ StartPosition = usingDirective.SpanStart,
+ Length = usingDirective.Span.Length
+ };
+
+ rootElement.AddChild(usingElement);
+ LogInfo($"Found using: {usingElement.Name}");
+ }
+
+ // Procesar todos los namespaces
+ foreach (var namespaceDeclaration in compilationUnit.Members.OfType())
+ {
+ ProcessNamespace(namespaceDeclaration, rootElement);
+ }
+
+ // Procesar clases que estén directamente en el root (sin namespace)
+ foreach (var typeDeclaration in compilationUnit.Members.OfType())
+ {
+ if (typeDeclaration is ClassDeclarationSyntax classDeclaration)
+ {
+ ProcessClass(classDeclaration, rootElement);
+ }
+ }
+
+ return rootElement;
+ }
+
+ ///
+ /// Procesa un namespace y sus miembros
+ ///
+ private void ProcessNamespace(NamespaceDeclarationSyntax namespaceDeclaration, CodeElement parent)
+ {
+ var namespaceElement = new CodeElement
+ {
+ Type = CodeElement.ElementType.Namespace,
+ Name = namespaceDeclaration.Name.ToString(),
+ OriginalText = namespaceDeclaration.ToString(),
+ StartPosition = namespaceDeclaration.SpanStart,
+ Length = namespaceDeclaration.Span.Length,
+ Indentation = ExtractIndentation(namespaceDeclaration),
+ LeadingComments = ExtractLeadingComments(namespaceDeclaration),
+ TrailingComments = ExtractTrailingComments(namespaceDeclaration)
+ };
+
+ parent.AddChild(namespaceElement);
+ LogInfo($"Found namespace: {namespaceElement.Name}");
+
+ // Procesar todas las clases dentro del namespace
+ foreach (var member in namespaceDeclaration.Members)
+ {
+ if (member is ClassDeclarationSyntax classDeclaration)
+ {
+ ProcessClass(classDeclaration, namespaceElement);
+ }
+ }
+ }
+
+ ///
+ /// Procesa una clase y sus miembros
+ ///
+ private void ProcessClass(ClassDeclarationSyntax classDeclaration, CodeElement parent)
+ {
+ var classElement = new CodeElement
+ {
+ Type = CodeElement.ElementType.Class,
+ Name = classDeclaration.Identifier.ToString(),
+ OriginalText = classDeclaration.ToString(),
+ StartPosition = classDeclaration.SpanStart,
+ Length = classDeclaration.Span.Length,
+ Indentation = ExtractIndentation(classDeclaration),
+ LeadingComments = ExtractLeadingComments(classDeclaration),
+ TrailingComments = ExtractTrailingComments(classDeclaration),
+ NamespaceName = parent.Type == CodeElement.ElementType.Namespace ? parent.Name : VIRTUAL_NAMESPACE
+ };
+
+ // Extraer información de herencia e interfaces
+ if (classDeclaration.BaseList != null)
+ {
+ foreach (var baseType in classDeclaration.BaseList.Types)
+ {
+ string baseTypeName = baseType.Type.ToString();
+ if (baseType == classDeclaration.BaseList.Types.First())
+ {
+ classElement.BaseClass = baseTypeName;
+ }
+ else
+ {
+ classElement.ImplementedInterfaces.Add(baseTypeName);
+ }
+ }
+ }
+
+ // Extraer modificadores
+ classElement.Modifiers = classDeclaration.Modifiers
+ .Select(m => m.ToString())
+ .ToArray();
+
+ // Extraer atributos
+ if (classDeclaration.AttributeLists.Any())
+ {
+ classElement.Attributes = classDeclaration.AttributeLists
+ .SelectMany(a => a.Attributes)
+ .Select(a => a.ToString())
+ .ToList();
+ }
+
+ parent.AddChild(classElement);
+ LogInfo($"Found class: {classElement.Name}");
+
+ // Procesar todos los miembros de la clase
+ foreach (var member in classDeclaration.Members)
+ {
+ if (member is FieldDeclarationSyntax fieldDeclaration)
+ {
+ ProcessField(fieldDeclaration, classElement);
+ }
+ else if (member is PropertyDeclarationSyntax propertyDeclaration)
+ {
+ ProcessProperty(propertyDeclaration, classElement);
+ }
+ else if (member is MethodDeclarationSyntax methodDeclaration)
+ {
+ ProcessMethod(methodDeclaration, classElement);
+ }
+ else if (member is ConstructorDeclarationSyntax constructorDeclaration)
+ {
+ ProcessConstructor(constructorDeclaration, classElement);
+ }
+ else if (member is ClassDeclarationSyntax nestedClassDeclaration)
+ {
+ // Clase anidada: procesamos recursivamente
+ ProcessClass(nestedClassDeclaration, classElement);
+ }
+ }
+ }
+
+ ///
+ /// Procesa un campo (field) de clase
+ ///
+ private void ProcessField(FieldDeclarationSyntax fieldDeclaration, CodeElement parent)
+ {
+ foreach (var variable in fieldDeclaration.Declaration.Variables)
+ {
+ var fieldElement = new CodeElement
+ {
+ Type = CodeElement.ElementType.Field,
+ Name = variable.Identifier.ToString(),
+ ReturnType = fieldDeclaration.Declaration.Type.ToString(),
+ OriginalText = fieldDeclaration.ToString(),
+ StartPosition = fieldDeclaration.SpanStart,
+ Length = fieldDeclaration.Span.Length,
+ Indentation = ExtractIndentation(fieldDeclaration),
+ LeadingComments = ExtractLeadingComments(fieldDeclaration),
+ TrailingComments = ExtractTrailingComments(fieldDeclaration),
+ NamespaceName = GetNamespaceFromParent(parent),
+ ClassName = parent.Name
+ };
+
+ // Extraer modificadores
+ fieldElement.Modifiers = fieldDeclaration.Modifiers
+ .Select(m => m.ToString())
+ .ToArray();
+
+ // Extraer inicializador si existe
+ if (variable.Initializer != null)
+ {
+ fieldElement.Initializer = variable.Initializer.Value.ToString();
+ }
+
+ // Extraer atributos
+ if (fieldDeclaration.AttributeLists.Any())
+ {
+ fieldElement.Attributes = fieldDeclaration.AttributeLists
+ .SelectMany(a => a.Attributes)
+ .Select(a => a.ToString())
+ .ToList();
+ }
+
+ // Generar firma normalizada
+ fieldElement.NormalizedSignature = $"{fieldElement.ReturnType} {fieldElement.Name}";
+
+ parent.AddChild(fieldElement);
+ LogInfo($"Found field: {fieldElement.Name} ({fieldElement.ReturnType})");
+ }
+ }
+
+ ///
+ /// Procesa una propiedad de clase
+ ///
+ private void ProcessProperty(PropertyDeclarationSyntax propertyDeclaration, CodeElement parent)
+ {
+ var propertyElement = new CodeElement
+ {
+ Type = CodeElement.ElementType.Property,
+ Name = propertyDeclaration.Identifier.ToString(),
+ ReturnType = propertyDeclaration.Type.ToString(),
+ OriginalText = propertyDeclaration.ToString(),
+ StartPosition = propertyDeclaration.SpanStart,
+ Length = propertyDeclaration.Span.Length,
+ Indentation = ExtractIndentation(propertyDeclaration),
+ LeadingComments = ExtractLeadingComments(propertyDeclaration),
+ TrailingComments = ExtractTrailingComments(propertyDeclaration),
+ NamespaceName = GetNamespaceFromParent(parent),
+ ClassName = parent.Name
+ };
+
+ // Extraer modificadores
+ propertyElement.Modifiers = propertyDeclaration.Modifiers
+ .Select(m => m.ToString())
+ .ToArray();
+
+ // Extraer accessors (get/set)
+ if (propertyDeclaration.AccessorList != null)
+ {
+ foreach (var accessor in propertyDeclaration.AccessorList.Accessors)
+ {
+ if (accessor.Keyword.Text == "get")
+ {
+ propertyElement.HasGetter = true;
+ propertyElement.GetterAccessibility = accessor.Modifiers.Any()
+ ? accessor.Modifiers.First().ToString()
+ : "";
+ }
+ else if (accessor.Keyword.Text == "set")
+ {
+ propertyElement.HasSetter = true;
+ propertyElement.SetterAccessibility = accessor.Modifiers.Any()
+ ? accessor.Modifiers.First().ToString()
+ : "";
+ }
+ }
+ }
+
+ // Extraer inicializador de propiedad (para propiedades con => o { get; set; } = value)
+ if (propertyDeclaration.Initializer != null)
+ {
+ propertyElement.Initializer = propertyDeclaration.Initializer.Value.ToString();
+ }
+
+ // Extraer atributos
+ if (propertyDeclaration.AttributeLists.Any())
+ {
+ propertyElement.Attributes = propertyDeclaration.AttributeLists
+ .SelectMany(a => a.Attributes)
+ .Select(a => a.ToString())
+ .ToList();
+ }
+
+ // Generar firma normalizada
+ propertyElement.NormalizedSignature = $"{propertyElement.ReturnType} {propertyElement.Name}";
+
+ parent.AddChild(propertyElement);
+ LogInfo($"Found property: {propertyElement.Name} ({propertyElement.ReturnType})");
+ }
+
+ ///
+ /// Procesa un método de clase
+ ///
+ private void ProcessMethod(MethodDeclarationSyntax methodDeclaration, CodeElement parent)
+ {
+ var methodElement = new CodeElement
+ {
+ Type = CodeElement.ElementType.Method,
+ Name = methodDeclaration.Identifier.ToString(),
+ ReturnType = methodDeclaration.ReturnType.ToString(),
+ OriginalText = methodDeclaration.ToString(),
+ StartPosition = methodDeclaration.SpanStart,
+ Length = methodDeclaration.Span.Length,
+ Indentation = ExtractIndentation(methodDeclaration),
+ LeadingComments = ExtractLeadingComments(methodDeclaration),
+ TrailingComments = ExtractTrailingComments(methodDeclaration),
+ NamespaceName = GetNamespaceFromParent(parent),
+ ClassName = parent.Name
+ };
+
+ // Extraer modificadores
+ methodElement.Modifiers = methodDeclaration.Modifiers
+ .Select(m => m.ToString())
+ .ToArray();
+
+ // Extraer parámetros
+ methodElement.Parameters = ExtractParametersFromMethod(methodDeclaration);
+
+ // Extraer parámetros de tipo para métodos genéricos
+ if (methodDeclaration.TypeParameterList != null)
+ {
+ methodElement.TypeParameters = methodDeclaration.TypeParameterList.Parameters
+ .Select(p => p.Identifier.ToString())
+ .ToList();
+ }
+
+ // Extraer atributos
+ if (methodDeclaration.AttributeLists.Any())
+ {
+ methodElement.Attributes = methodDeclaration.AttributeLists
+ .SelectMany(a => a.Attributes)
+ .Select(a => a.ToString())
+ .ToList();
+ }
+
+ // Generar firma normalizada según requerimiento estricto
+ GenerateStrictMethodSignature(methodElement);
+
+ parent.AddChild(methodElement);
+ LogInfo($"Found method: {methodElement.Name} ({methodElement.NormalizedSignature})");
+ }
+
+ ///
+ /// Procesa un constructor de clase
+ ///
+ private void ProcessConstructor(ConstructorDeclarationSyntax constructorDeclaration, CodeElement parent)
+ {
+ var constructorElement = new CodeElement
+ {
+ Type = CodeElement.ElementType.Constructor,
+ Name = constructorDeclaration.Identifier.ToString(),
+ OriginalText = constructorDeclaration.ToString(),
+ StartPosition = constructorDeclaration.SpanStart,
+ Length = constructorDeclaration.Span.Length,
+ Indentation = ExtractIndentation(constructorDeclaration),
+ LeadingComments = ExtractLeadingComments(constructorDeclaration),
+ TrailingComments = ExtractTrailingComments(constructorDeclaration),
+ NamespaceName = GetNamespaceFromParent(parent),
+ ClassName = parent.Name
+ };
+
+ // Extraer modificadores
+ constructorElement.Modifiers = constructorDeclaration.Modifiers
+ .Select(m => m.ToString())
+ .ToArray();
+
+ // Extraer parámetros
+ constructorElement.Parameters = ExtractParametersFromConstructor(constructorDeclaration);
+
+ // Extraer atributos
+ if (constructorDeclaration.AttributeLists.Any())
+ {
+ constructorElement.Attributes = constructorDeclaration.AttributeLists
+ .SelectMany(a => a.Attributes)
+ .Select(a => a.ToString())
+ .ToList();
+ }
+
+ // Generar firma normalizada
+ GenerateStrictConstructorSignature(constructorElement);
+
+ parent.AddChild(constructorElement);
+ LogInfo($"Found constructor: {constructorElement.NormalizedSignature}");
+ }
+
+ ///
+ /// Extrae los parámetros de un método
+ ///
+ private List ExtractParametersFromMethod(MethodDeclarationSyntax methodDeclaration)
+ {
+ var parameters = new List();
+
+ if (methodDeclaration.ParameterList != null)
+ {
+ foreach (var param in methodDeclaration.ParameterList.Parameters)
+ {
+ var paramInfo = new ParameterInfo
+ {
+ Type = param.Type?.ToString() ?? "var",
+ Name = param.Identifier.ToString(),
+ HasDefaultValue = param.Default != null
+ };
+
+ // Capturar modificadores exactos (ref, out, in, params)
+ if (param.Modifiers.Any())
+ {
+ paramInfo.Modifier = string.Join(" ", param.Modifiers) + " ";
+ }
+
+ // Capturar valores por defecto si existen
+ if (param.Default != null)
+ {
+ paramInfo.DefaultValue = param.Default.Value.ToString();
+ }
+
+ parameters.Add(paramInfo);
+ }
+ }
+
+ return parameters;
+ }
+
+ ///
+ /// Extrae los parámetros de un constructor
+ ///
+ private List ExtractParametersFromConstructor(ConstructorDeclarationSyntax constructorDeclaration)
+ {
+ var parameters = new List();
+
+ if (constructorDeclaration.ParameterList != null)
+ {
+ foreach (var param in constructorDeclaration.ParameterList.Parameters)
+ {
+ var paramInfo = new ParameterInfo
+ {
+ Type = param.Type?.ToString() ?? "var",
+ Name = param.Identifier.ToString(),
+ HasDefaultValue = param.Default != null
+ };
+
+ // Capturar modificadores exactos (ref, out, in, params)
+ if (param.Modifiers.Any())
+ {
+ paramInfo.Modifier = string.Join(" ", param.Modifiers) + " ";
+ }
+
+ // Capturar valores por defecto si existen
+ if (param.Default != null)
+ {
+ paramInfo.DefaultValue = param.Default.Value.ToString();
+ }
+
+ parameters.Add(paramInfo);
+ }
+ }
+
+ return parameters;
+ }
+
+ ///
+ /// Genera una firma normalizada estricta para un método
+ ///
+ private void GenerateStrictMethodSignature(CodeElement methodElement)
+ {
+ // Construir lista de parámetros con tipo y nombre
+ var paramList = methodElement.Parameters.Select(p => $"{p.Modifier}{p.Type} {p.Name}").ToList();
+ string paramString = string.Join(", ", paramList);
+
+ // Incluir tipo genérico si existe
+ string typeParams = "";
+ if (methodElement.TypeParameters != null && methodElement.TypeParameters.Count > 0)
+ {
+ typeParams = $"<{string.Join(", ", methodElement.TypeParameters)}>";
+ }
+
+ // Incluir todo: tipo de retorno, nombre del método, parámetros de tipo y firma completa de parámetros
+ methodElement.NormalizedSignature = $"{methodElement.ReturnType} {methodElement.Name}{typeParams}({paramString})";
+ }
+
+ ///
+ /// Genera una firma normalizada estricta para un constructor
+ ///
+ private void GenerateStrictConstructorSignature(CodeElement constructorElement)
+ {
+ // Construir lista de parámetros con tipo y nombre
+ var paramList = constructorElement.Parameters.Select(p => $"{p.Modifier}{p.Type} {p.Name}").ToList();
+ string paramString = string.Join(", ", paramList);
+
+ // Para constructores: nombre de clase + parámetros completos
+ constructorElement.NormalizedSignature = $"{constructorElement.Name}({paramString})";
+ }
+
+ ///
+ /// Extrae la indentación de un nodo sintáctico
+ ///
+ private string ExtractIndentation(SyntaxNode node)
+ {
+ var leadingTrivia = node.GetLeadingTrivia();
+ var whitespaceTrivia = leadingTrivia.LastOrDefault(t => t.IsKind(SyntaxKind.WhitespaceTrivia));
+ return whitespaceTrivia.ToString();
+ }
+
+ ///
+ /// Extrae comentarios que preceden a un nodo
+ ///
+ private string ExtractLeadingComments(SyntaxNode node)
+ {
+ var leadingTrivia = node.GetLeadingTrivia();
+ var comments = leadingTrivia.Where(t =>
+ t.IsKind(SyntaxKind.SingleLineCommentTrivia) ||
+ t.IsKind(SyntaxKind.MultiLineCommentTrivia) ||
+ t.IsKind(SyntaxKind.DocumentationCommentExteriorTrivia));
+
+ return string.Join(Environment.NewLine, comments);
+ }
+
+ ///
+ /// Extrae comentarios que siguen a un nodo
+ ///
+ private string ExtractTrailingComments(SyntaxNode node)
+ {
+ var trailingTrivia = node.GetTrailingTrivia();
+ var comments = trailingTrivia.Where(t =>
+ t.IsKind(SyntaxKind.SingleLineCommentTrivia) ||
+ t.IsKind(SyntaxKind.MultiLineCommentTrivia));
+
+ return string.Join(Environment.NewLine, comments);
+ }
+
+ ///
+ /// Obtiene el nombre del namespace desde el padre de un elemento
+ ///
+ private string GetNamespaceFromParent(CodeElement parent)
+ {
+ if (parent.Type == CodeElement.ElementType.Namespace)
+ return parent.Name;
+
+ if (parent.NamespaceName != null)
+ return parent.NamespaceName;
+
+ return VIRTUAL_NAMESPACE;
+ }
+
+ ///
+ /// Construye índices para búsqueda rápida de elementos
+ ///
+ private void BuildSearchIndices(CodeElement root)
+ {
+ // Limpiar índices existentes
+ _methodsBySignature.Clear();
+ _propertiesByName.Clear();
+ _fieldsByName.Clear();
+ _classesByName.Clear();
+ _namespacesByName.Clear();
+ _usingsByName.Clear();
+
+ // Recorrer árbol y poblar índices
+ BuildIndicesRecursive(root);
+ }
+
+ ///
+ /// Construye índices de búsqueda recursivamente
+ ///
+ private void BuildIndicesRecursive(CodeElement element)
+ {
+ // Agregar elemento al índice apropiado
+ switch (element.Type)
+ {
+ case CodeElement.ElementType.Method:
+ _methodsBySignature[element.NormalizedSignature] = element;
+ break;
+ case CodeElement.ElementType.Constructor:
+ _methodsBySignature[element.NormalizedSignature] = element;
+ break;
+ case CodeElement.ElementType.Property:
+ _propertiesByName[element.Name] = element;
+ break;
+ case CodeElement.ElementType.Field:
+ _fieldsByName[element.Name] = element;
+ break;
+ case CodeElement.ElementType.Class:
+ _classesByName[element.Name] = element;
+ break;
+ case CodeElement.ElementType.Namespace:
+ _namespacesByName[element.Name] = element;
+ break;
+ case CodeElement.ElementType.Using:
+ _usingsByName[element.Name] = element;
+ break;
+ }
+
+ // Procesar recursivamente hijos
+ foreach (var child in element.Children)
+ {
+ BuildIndicesRecursive(child);
+ }
+ }
+
+ #endregion
+
+ #region Métodos Privados para Merge de Código
+
+ ///
+ /// Realiza el merge de dos elementos de código y sus hijos
+ ///
+ private CodeElement MergeElements(CodeElement master, CodeElement snippet)
+ {
+ LogInfo($"Merging elements: {master.Type} and {snippet.Type}");
+
+ // El resultado base será una copia del elemento master
+ var result = master;
+
+ // Merge de usings
+ MergeUsings(result, snippet);
+
+ // Merge de namespaces
+ MergeNamespaces(result, snippet);
+
+ // Merge de clases
+ MergeClasses(result, snippet);
+
+ LogInfo("Element merge completed");
+ return result;
+ }
+
+ ///
+ /// Realiza el merge de directivas using
+ ///
+ private void MergeUsings(CodeElement master, CodeElement snippet)
+ {
+ var masterUsings = master.GetAllDescendantsOfType(CodeElement.ElementType.Using)
+ .Select(u => u.Name)
+ .ToHashSet();
+
+ foreach (var usingElement in snippet.GetAllDescendantsOfType(CodeElement.ElementType.Using))
+ {
+ if (!masterUsings.Contains(usingElement.Name))
+ {
+ // Nuevo using: agregar al master
+ var rootOrNamespace = master.Children.FirstOrDefault(c => c.Type == CodeElement.ElementType.Namespace) ?? master;
+ rootOrNamespace.AddChild(usingElement);
+ LogAddition(usingElement);
+ masterUsings.Add(usingElement.Name);
+ }
+ }
+ }
+
+ ///
+ /// Realiza el merge de namespaces
+ ///
+ private void MergeNamespaces(CodeElement master, CodeElement snippet)
+ {
+ var masterNamespaces = master.Children
+ .Where(c => c.Type == CodeElement.ElementType.Namespace)
+ .ToDictionary(n => n.Name);
+
+ foreach (var snippetNamespace in snippet.Children.Where(c => c.Type == CodeElement.ElementType.Namespace))
+ {
+ if (snippetNamespace.IsVirtualNamespace)
+ {
+ // Para namespace virtual, solo procesamos su contenido
+ foreach (var snippetClass in snippetNamespace.Children.Where(c => c.Type == CodeElement.ElementType.Class))
+ {
+ ProcessSnippetClass(master, snippetClass);
+ }
+ }
+ else if (masterNamespaces.TryGetValue(snippetNamespace.Name, out var masterNamespace))
+ {
+ // Namespace existente: merge de su contenido
+ foreach (var snippetClass in snippetNamespace.Children.Where(c => c.Type == CodeElement.ElementType.Class))
+ {
+ ProcessSnippetClass(masterNamespace, snippetClass);
+ }
+ }
+ else
+ {
+ // Nuevo namespace: agregar al master
+ master.AddChild(snippetNamespace);
+ LogAddition(snippetNamespace);
+ }
+ }
+ }
+
+ ///
+ /// Procesa una clase del snippet, ya sea agregándola o mergeando con una existente
+ ///
+ private void ProcessSnippetClass(CodeElement parent, CodeElement snippetClass)
+ {
+ // Buscar clase existente en el master
+ var masterClass = parent.Children
+ .FirstOrDefault(c => c.Type == CodeElement.ElementType.Class && c.Name == snippetClass.Name);
+
+ if (masterClass == null)
+ {
+ // Clase no existe: agregarla completa
+ if (!snippetClass.IsVirtualClass)
+ {
+ parent.AddChild(snippetClass);
+ LogAddition(snippetClass);
+ }
+ else
+ {
+ // Para clase virtual, solo procesamos su contenido directamente en el parent
+ MergeClassMembers(parent, snippetClass);
+ }
+ }
+ else
+ {
+ // Clase ya existe: hacer merge de miembros
+ MergeClassMembers(masterClass, snippetClass);
+ }
+ }
+
+ ///
+ /// Merge de miembros de una clase (fields, properties, methods, constructors)
+ ///
+ private void MergeClassMembers(CodeElement masterClass, CodeElement snippetClass)
+ {
+ // Merge de fields
+ MergeFields(masterClass, snippetClass);
+
+ // Merge de propiedades
+ MergeProperties(masterClass, snippetClass);
+
+ // Merge de métodos
+ MergeMethods(masterClass, snippetClass);
+
+ // Merge de constructores
+ MergeConstructors(masterClass, snippetClass);
+
+ // Clases anidadas
+ MergeNestedClasses(masterClass, snippetClass);
+ }
+
+ ///
+ /// Merge de campos (fields) de clase
+ ///
+ private void MergeFields(CodeElement masterClass, CodeElement snippetClass)
+ {
+ var masterFields = masterClass.Children
+ .Where(c => c.Type == CodeElement.ElementType.Field)
+ .ToDictionary(f => f.Name);
+
+ foreach (var snippetField in snippetClass.Children.Where(c => c.Type == CodeElement.ElementType.Field))
+ {
+ if (!masterFields.ContainsKey(snippetField.Name))
+ {
+ // Field no existe: agregarlo
+ masterClass.AddChild(snippetField);
+ LogAddition(snippetField);
+ }
+ // No reemplazamos fields existentes
+ }
+ }
+
+ ///
+ /// Merge de propiedades de clase
+ ///
+ private void MergeProperties(CodeElement masterClass, CodeElement snippetClass)
+ {
+ var masterProperties = masterClass.Children
+ .Where(c => c.Type == CodeElement.ElementType.Property)
+ .ToDictionary(p => p.Name);
+
+ foreach (var snippetProperty in snippetClass.Children.Where(c => c.Type == CodeElement.ElementType.Property))
+ {
+ if (!masterProperties.ContainsKey(snippetProperty.Name))
+ {
+ // Propiedad no existe: agregarla
+ masterClass.AddChild(snippetProperty);
+ LogAddition(snippetProperty);
+ }
+ // No reemplazamos propiedades existentes
+ }
+ }
+
+ ///
+ /// Merge de métodos de clase
+ ///
+ private void MergeMethods(CodeElement masterClass, CodeElement snippetClass)
+ {
+ var masterMethods = masterClass.Children
+ .Where(c => c.Type == CodeElement.ElementType.Method)
+ .ToDictionary(m => m.NormalizedSignature);
+
+ foreach (var snippetMethod in snippetClass.Children.Where(c => c.Type == CodeElement.ElementType.Method))
+ {
+ if (masterMethods.TryGetValue(snippetMethod.NormalizedSignature, out var existingMethod))
+ {
+ // Método existe: reemplazarlo
+ masterClass.Children.Remove(existingMethod);
+ masterClass.AddChild(snippetMethod);
+ LogReplacement(existingMethod, snippetMethod);
+ }
+ else
+ {
+ // Método no existe: agregarlo
+ masterClass.AddChild(snippetMethod);
+ LogAddition(snippetMethod);
+ }
+ }
+ }
+
+ ///
+ /// Merge de constructores de clase
+ ///
+ private void MergeConstructors(CodeElement masterClass, CodeElement snippetClass)
+ {
+ var masterConstructors = masterClass.Children
+ .Where(c => c.Type == CodeElement.ElementType.Constructor)
+ .ToDictionary(m => m.NormalizedSignature);
+
+ foreach (var snippetConstructor in snippetClass.Children.Where(c => c.Type == CodeElement.ElementType.Constructor))
+ {
+ if (masterConstructors.TryGetValue(snippetConstructor.NormalizedSignature, out var existingConstructor))
+ {
+ // Constructor existe: reemplazarlo
+ masterClass.Children.Remove(existingConstructor);
+ masterClass.AddChild(snippetConstructor);
+ LogReplacement(existingConstructor, snippetConstructor);
+ }
+ else
+ {
+ // Constructor no existe: agregarlo
+ masterClass.AddChild(snippetConstructor);
+ LogAddition(snippetConstructor);
+ }
+ }
+ }
+
+ ///
+ /// Merge de clases anidadas
+ ///
+ private void MergeNestedClasses(CodeElement masterClass, CodeElement snippetClass)
+ {
+ var masterNestedClasses = masterClass.Children
+ .Where(c => c.Type == CodeElement.ElementType.Class)
+ .ToDictionary(c => c.Name);
+
+ foreach (var snippetNestedClass in snippetClass.Children.Where(c => c.Type == CodeElement.ElementType.Class))
+ {
+ if (masterNestedClasses.TryGetValue(snippetNestedClass.Name, out var existingClass))
+ {
+ // Clase anidada existe: merge recursivo
+ MergeClassMembers(existingClass, snippetNestedClass);
+ }
+ else
+ {
+ // Clase anidada no existe: agregarla
+ masterClass.AddChild(snippetNestedClass);
+ LogAddition(snippetNestedClass);
+ }
+ }
+ }
+
+ ///
+ /// Reconstruye el código a partir de un árbol de CodeElement
+ ///
+ private string ReconstructCode(CodeElement root)
+ {
+ var sb = new StringBuilder();
+
+ // Reconstruir usando desde el root
+ foreach (var usingElement in root.Children.Where(c => c.Type == CodeElement.ElementType.Using))
+ {
+ sb.AppendLine(usingElement.OriginalText);
+ }
+
+ // Reconstruir namespaces y su contenido
+ foreach (var namespaceElement in root.Children.Where(c => c.Type == CodeElement.ElementType.Namespace))
+ {
+ // Skip virtual namespace
+ if (namespaceElement.IsVirtualNamespace)
+ {
+ // Reconstruir directamente el contenido del namespace virtual
+ foreach (var classElement in namespaceElement.Children)
+ {
+ if (classElement.IsVirtualClass)
+ {
+ // Si es clase virtual, solo añadir su contenido
+ foreach (var member in classElement.Children)
+ {
+ sb.AppendLine(member.OriginalText);
+ }
+ }
+ else
+ {
+ sb.AppendLine(classElement.OriginalText);
+ }
+ }
+ }
+ else
+ {
+ // Namespace normal - reconstruir completo
+ sb.AppendLine(namespaceElement.OriginalText);
+ }
+ }
+
+ // Reconstruir clases que estén directamente en el root
+ foreach (var classElement in root.Children.Where(c => c.Type == CodeElement.ElementType.Class))
+ {
+ if (!classElement.IsVirtualClass)
+ {
+ sb.AppendLine(classElement.OriginalText);
+ }
+ }
+
+ return sb.ToString();
+ }
+
+ #endregion
+
+ #region Métodos para Logging
+
+ ///
+ /// Registra una operación de adición
+ ///
+ private void LogAddition(CodeElement element)
+ {
+ _log.AppendLine($"ADDED: {element.Type} {element.Name}");
+ }
+
+ ///
+ /// Registra una operación de reemplazo
+ ///
+ private void LogReplacement(CodeElement oldElement, CodeElement newElement)
+ {
+ _log.AppendLine($"REPLACED: {oldElement.Type} {oldElement.Name} with new implementation");
+ }
+
+ ///
+ /// Registra un mensaje informativo
+ ///
+ private void LogInfo(string message)
+ {
+ _log.AppendLine($"INFO: {message}");
+ }
+
+ ///
+ /// Registra un mensaje de error
+ ///
+ private void LogError(string message)
+ {
+ _log.AppendLine($"ERROR: {message}");
+ }
+
+ #endregion
+}
+
+///
+/// Elemento de código analizado
+///
+public class CodeElement
+{
+ public enum ElementType
+ {
+ Using,
+ Namespace,
+ Class,
+ Method,
+ Property,
+ Field,
+ Constructor,
+ Other
+ }
+
+ #region Propiedades Básicas
+
+ // Propiedades originales
+ public ElementType Type { get; set; }
+ public string Name { get; set; }
+ public string NamespaceName { get; set; }
+ public string ClassName { get; set; }
+ public string NormalizedSignature { get; set; }
+ public string OriginalText { get; set; }
+ public int StartPosition { get; set; }
+ public int Length { get; set; }
+
+ // Constantes para namespace y clase virtual
+ private const string VIRTUAL_NAMESPACE = "__VirtualNamespace__";
+ private const string VIRTUAL_CLASS = "__VirtualClass__";
+
+ public bool IsVirtualNamespace => NamespaceName == VIRTUAL_NAMESPACE;
+ public bool IsVirtualClass => ClassName == VIRTUAL_CLASS;
+
+ #endregion
+
+ #region Relaciones Jerárquicas
+
+ // Relaciones jerárquicas
+ public CodeElement Parent { get; set; }
+ public List Children { get; set; } = new List();
+
+ // Nivel de anidamiento (útil para visualización)
+ public int NestingLevel => Parent == null ? 0 : Parent.NestingLevel + 1;
+
+ // Path completo en la jerarquía
+ public string FullPath
+ {
+ get
+ {
+ if (Parent == null) return Name;
+ return $"{Parent.FullPath}.{Name}";
+ }
+ }
+
+ #endregion
+
+ #region Metadata para Reconstrucción
+
+ // Metadata para reconstrucción
+ public string[] Modifiers { get; set; }
+ public List Attributes { get; set; } = new List();
+
+ // Comentarios y trivia
+ public string LeadingComments { get; set; }
+ public string TrailingComments { get; set; }
+ public string Indentation { get; set; }
+
+ // Parámetros de tipo para genéricos
+ public List TypeParameters { get; set; } = new List();
+
+ // Para clases: información de herencia e interfaces
+ public string BaseClass { get; set; }
+ public List ImplementedInterfaces { get; set; } = new List();
+
+ // Para métodos y propiedades: tipo de retorno específico
+ public string ReturnType { get; set; }
+
+ // Para propiedades: accessors
+ public bool HasGetter { get; set; }
+ public bool HasSetter { get; set; }
+ public string GetterAccessibility { get; set; }
+ public string SetterAccessibility { get; set; }
+
+ // Para campos: inicializador
+ public string Initializer { get; set; }
+
+ // Para métodos y constructores: parámetros
+ public List Parameters { get; set; } = new List();
+
+ #endregion
+
+ #region Métodos
+
+ ///
+ /// Agrega un elemento hijo con manejo automático de relación Parent
+ ///
+ public void AddChild(CodeElement child)
+ {
+ Children.Add(child);
+ child.Parent = this;
+ }
+
+ ///
+ /// Busca un elemento hijo por nombre y tipo
+ ///
+ public CodeElement FindChild(string name, ElementType type)
+ {
+ return Children.FirstOrDefault(c => c.Name == name && c.Type == type);
+ }
+
+ ///
+ /// Encuentra todos los elementos hijos de un tipo específico
+ ///
+ public IEnumerable FindAllChildren(ElementType type)
+ {
+ return Children.Where(c => c.Type == type);
+ }
+
+ ///
+ /// Busca en toda la jerarquía del elemento según un predicado
+ ///
+ public CodeElement FindInHierarchy(Func predicate)
+ {
+ if (predicate(this)) return this;
+
+ foreach (var child in Children)
+ {
+ var result = child.FindInHierarchy(predicate);
+ if (result != null) return result;
+ }
+
+ return null;
+ }
+
+ ///
+ /// Obtiene todos los elementos descendientes de un tipo específico
+ ///
+ public IEnumerable GetAllDescendantsOfType(ElementType type)
+ {
+ var result = new List();
+ CollectDescendantsOfType(this, type, result);
+ return result;
+ }
+
+ ///
+ /// Método auxiliar para recopilar descendientes recursivamente
+ ///
+ private void CollectDescendantsOfType(CodeElement element, ElementType type, List result)
+ {
+ foreach (var child in element.Children)
+ {
+ if (child.Type == type)
+ result.Add(child);
+
+ CollectDescendantsOfType(child, type, result);
+ }
+ }
+
+ ///
+ /// Representación en cadena del elemento
+ ///
+ public override string ToString()
+ {
+ return $"{Type}: {NormalizedSignature ?? Name}";
+ }
+
+ #endregion
+}
+
+///
+/// Información de un parámetro de método o constructor
+///
+public class ParameterInfo
+{
+ public string Type { get; set; }
+ public string Name { get; set; }
+ public string Modifier { get; set; } = ""; // ref, out, in, params
+ public bool HasDefaultValue { get; set; }
+ public string DefaultValue { get; set; }
+
+ public override string ToString()
+ {
+ return $"{Modifier}{Type} {Name}{(HasDefaultValue ? $" = {DefaultValue}" : "")}";
+ }
+}
\ No newline at end of file
diff --git a/CodeMergerNG.csproj b/CodeMergerNG.csproj
new file mode 100644
index 0000000..f876c82
--- /dev/null
+++ b/CodeMergerNG.csproj
@@ -0,0 +1,18 @@
+
+
+
+ WinExe
+ net8.0-windows
+ enable
+ enable
+ true
+
+
+
+
+
+
+
+
+
+
diff --git a/CodeMergerNG.sln b/CodeMergerNG.sln
new file mode 100644
index 0000000..021a1d2
--- /dev/null
+++ b/CodeMergerNG.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.13.35806.99 d17.13
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeMergerNG", "CodeMergerNG.csproj", "{E34F5B22-68C1-454A-A5FB-6FE89352C3C9}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {E34F5B22-68C1-454A-A5FB-6FE89352C3C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E34F5B22-68C1-454A-A5FB-6FE89352C3C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E34F5B22-68C1-454A-A5FB-6FE89352C3C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E34F5B22-68C1-454A-A5FB-6FE89352C3C9}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {4FB3A6C1-5305-4483-A6A9-2322D8361527}
+ EndGlobalSection
+EndGlobal
diff --git a/Controls/CodeStructureTreeView.xaml b/Controls/CodeStructureTreeView.xaml
new file mode 100644
index 0000000..f0f9a3f
--- /dev/null
+++ b/Controls/CodeStructureTreeView.xaml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Controls/CodeStructureTreeView.xaml.cs b/Controls/CodeStructureTreeView.xaml.cs
new file mode 100644
index 0000000..2bf32e4
--- /dev/null
+++ b/Controls/CodeStructureTreeView.xaml.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace CodeMergerNG.Controls
+{
+ ///
+ /// Interaction logic for CodeStructureTreeView.xaml
+ ///
+ public partial class CodeStructureTreeView : UserControl
+ {
+ public CodeStructureTreeView()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/Controls/SimpleMergeLogControl.xaml b/Controls/SimpleMergeLogControl.xaml
new file mode 100644
index 0000000..e5945a7
--- /dev/null
+++ b/Controls/SimpleMergeLogControl.xaml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Controls/SimpleMergeLogControl.xaml.cs b/Controls/SimpleMergeLogControl.xaml.cs
new file mode 100644
index 0000000..dc6d115
--- /dev/null
+++ b/Controls/SimpleMergeLogControl.xaml.cs
@@ -0,0 +1,114 @@
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Media;
+using DocumentFormat.OpenXml.VariantTypes;
+using Microsoft.Win32;
+
+namespace CodeMerger.Controls
+{
+ public partial class SimpleMergeLogControl : UserControl
+ {
+ private static readonly SolidColorBrush InfoBrush = Brushes.Black;
+ private static readonly SolidColorBrush WarningBrush = Brushes.Orange;
+ private static readonly SolidColorBrush ErrorBrush = Brushes.Red;
+ private static readonly SolidColorBrush AddedBrush = Brushes.Green;
+ private static readonly SolidColorBrush ReplacedBrush = Brushes.Blue;
+
+ public SimpleMergeLogControl()
+ {
+ InitializeComponent();
+ }
+
+ public void LogInfo(string message)
+ {
+ AppendText($"[INFO] {message}", InfoBrush);
+ }
+
+ public void LogWarning(string message)
+ {
+ AppendText($"[WARNING] {message}", WarningBrush);
+ }
+
+ public void LogError(string message)
+ {
+ AppendText($"[ERROR] {message}", ErrorBrush);
+ }
+
+ public void LogMergeDetails(string mergeLog)
+ {
+ AppendText("--- MERGE DETAILS ---", InfoBrush);
+
+ // Procesar log de merge y colorear según el tipo
+ foreach (var line in mergeLog.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries))
+ {
+ if (line.StartsWith("ADDED:"))
+ {
+ AppendText(line, AddedBrush);
+ }
+ else if (line.StartsWith("REPLACED:"))
+ {
+ AppendText(line, ReplacedBrush);
+ }
+ else if (line.StartsWith("ERROR:"))
+ {
+ AppendText(line, ErrorBrush);
+ }
+ else
+ {
+ AppendText(line, InfoBrush);
+ }
+ }
+
+ AppendText("---------------------", InfoBrush);
+ }
+
+ private void AppendText(string text, Brush color)
+ {
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ var paragraph = new Paragraph();
+ paragraph.Inlines.Add(new Run(text) { Foreground = color });
+ rtbLog.Document.Blocks.Add(paragraph);
+
+ // Auto-scroll al final
+ rtbLog.ScrollToEnd();
+ });
+ }
+
+ private void ClearLog_Click(object sender, RoutedEventArgs e)
+ {
+ rtbLog.Document.Blocks.Clear();
+ }
+
+ private void CopyLog_Click(object sender, RoutedEventArgs e)
+ {
+ Clipboard.SetText(new TextRange(rtbLog.Document.ContentStart, rtbLog.Document.ContentEnd).Text);
+ LogInfo("Log copiado al portapapeles");
+ }
+
+ private void SaveLog_Click(object sender, RoutedEventArgs e)
+ {
+ SaveFileDialog saveFileDialog = new SaveFileDialog
+ {
+ Filter = "Archivos de texto (*.txt)|*.txt|Todos los archivos (*.*)|*.*",
+ FileName = "CodeMerger_Log.txt"
+ };
+
+ if (saveFileDialog.ShowDialog() == true)
+ {
+ try
+ {
+ string text = new TextRange(rtbLog.Document.ContentStart, rtbLog.Document.ContentEnd).Text;
+ System.IO.File.WriteAllText(saveFileDialog.FileName, text);
+ LogInfo($"Log guardado en: {saveFileDialog.FileName}");
+ }
+ catch (Exception ex)
+ {
+ LogError($"Error al guardar el log: {ex.Message}");
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Controls/TestExamplesControl.xaml b/Controls/TestExamplesControl.xaml
new file mode 100644
index 0000000..ca86e63
--- /dev/null
+++ b/Controls/TestExamplesControl.xaml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Controls/TestExamplesControl.xaml.cs b/Controls/TestExamplesControl.xaml.cs
new file mode 100644
index 0000000..4ca93e9
--- /dev/null
+++ b/Controls/TestExamplesControl.xaml.cs
@@ -0,0 +1,120 @@
+using System;
+using System.Collections.Generic;
+using System.Windows;
+using System.Windows.Controls;
+using CodeMerger.TestExamples;
+
+namespace CodeMerger.Controls
+{
+ public partial class TestExamplesControl : UserControl
+ {
+ public class TestExample
+ {
+ public string Description { get; set; }
+ public string Details { get; set; }
+ public string MasterCode { get; set; }
+ public string SnippetCode { get; set; }
+ }
+
+ private List _examples = new List();
+
+ public TestExamplesControl()
+ {
+ InitializeComponent();
+ LoadExamplesList();
+ }
+
+ private void LoadExamplesList()
+ {
+ _examples = new List
+ {
+ new TestExample
+ {
+ Description = "Merge Simple",
+ Details = "Añade un método a una clase simple",
+ MasterCode = TestExampleCode.SimpleMaster,
+ SnippetCode = TestExampleCode.SimpleSnippet
+ },
+ new TestExample
+ {
+ Description = "Manejo de Namespace",
+ Details = "Merge de código con diferentes namespaces",
+ MasterCode = TestExampleCode.NamespaceHandlingMaster,
+ SnippetCode = TestExampleCode.NamespaceHandlingSnippet
+ },
+ new TestExample
+ {
+ Description = "Elementos Virtuales",
+ Details = "Merge de código sin namespace ni clase",
+ MasterCode = TestExampleCode.VirtualElementsMaster,
+ SnippetCode = TestExampleCode.VirtualElementsSnippet
+ },
+ new TestExample
+ {
+ Description = "Reemplazo de Métodos",
+ Details = "Reemplaza un método existente con una implementación mejorada",
+ MasterCode = TestExampleCode.MethodReplacementMaster,
+ SnippetCode = TestExampleCode.MethodReplacementSnippet
+ },
+ new TestExample
+ {
+ Description = "Merge Complejo",
+ Details = "Merge de una clase genérica con múltiples métodos y propiedades",
+ MasterCode = TestExampleCode.ComplexMergeMaster,
+ SnippetCode = TestExampleCode.ComplexMergeSnippet
+ },
+ new TestExample
+ {
+ Description = "Clases Múltiples",
+ Details = "Merge de código con múltiples clases y herencia",
+ MasterCode = TestExampleCode.MultipleClassesMaster,
+ SnippetCode = TestExampleCode.MultipleClassesSnippet
+ },
+ new TestExample
+ {
+ Description = "Clases Anidadas",
+ Details = "Merge con clases anidadas y estructuras complejas",
+ MasterCode = TestExampleCode.NestedClassesMaster,
+ SnippetCode = TestExampleCode.NestedClassesSnippet
+ },
+ new TestExample
+ {
+ Description = "Comentarios y Formato",
+ Details = "Merge preservando comentarios XML y formato",
+ MasterCode = TestExampleCode.CommentsFormatMaster,
+ SnippetCode = TestExampleCode.CommentsFormatSnippet
+ }
+ };
+
+ lvExamples.ItemsSource = _examples;
+ }
+
+ private void LvExamples_SelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ // No hacer nada, solo permitir selección
+ }
+
+ private void LoadExample_Click(object sender, RoutedEventArgs e)
+ {
+ var selectedExample = lvExamples.SelectedItem as TestExample;
+ if (selectedExample == null)
+ {
+ MessageBox.Show("Por favor, seleccione un ejemplo primero", "Selección Requerida",
+ MessageBoxButton.OK, MessageBoxImage.Information);
+ return;
+ }
+
+ var mainWindow = Window.GetWindow(this) as MainWindow;
+ if (mainWindow != null)
+ {
+ // Cargar el ejemplo en los editores
+ mainWindow.txtOriginalCode.Text = selectedExample.MasterCode;
+ mainWindow.txtLLMCode.Text = selectedExample.SnippetCode;
+
+ // Notificar
+ var logControl = mainWindow.logControl;
+ logControl?.LogInfo($"Ejemplo cargado: {selectedExample.Description}");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/MainWindow.xaml b/MainWindow.xaml
new file mode 100644
index 0000000..aeb75d3
--- /dev/null
+++ b/MainWindow.xaml
@@ -0,0 +1,136 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs
new file mode 100644
index 0000000..c96f8db
--- /dev/null
+++ b/MainWindow.xaml.cs
@@ -0,0 +1,110 @@
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using CodeMerger.ViewModels;
+using ICSharpCode.AvalonEdit;
+using ICSharpCode.AvalonEdit.Highlighting;
+
+namespace CodeMerger
+{
+ public partial class MainWindow : Window
+ {
+ private MainWindowViewModel _viewModel;
+
+ public MainWindow()
+ {
+ InitializeComponent();
+
+ _viewModel = new MainWindowViewModel();
+ DataContext = _viewModel;
+ }
+
+ private void txtOriginalCode_Loaded(object sender, RoutedEventArgs e)
+ {
+ ConfigureTextEditor(txtOriginalCode);
+ }
+
+ private void txtLLMCode_Loaded(object sender, RoutedEventArgs e)
+ {
+ ConfigureTextEditor(txtLLMCode);
+ }
+
+ private void txtMergedCode_Loaded(object sender, RoutedEventArgs e)
+ {
+ ConfigureTextEditor(txtMergedCode);
+ }
+
+ private void ConfigureTextEditor(TextEditor editor)
+ {
+ editor.Options.EnableHyperlinks = false;
+ editor.Options.EnableEmailHyperlinks = false;
+ editor.Options.EnableVirtualSpace = false;
+ editor.Options.ShowSpaces = false;
+ editor.Options.ShowTabs = false;
+ editor.Options.ShowEndOfLine = false;
+ editor.Options.ShowColumnRuler = false;
+ editor.Options.EnableRectangularSelection = true;
+ editor.Options.EnableTextDragDrop = true;
+ editor.WordWrap = true;
+
+ // Configurar syntax highlighting para C#
+ editor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition("C#");
+
+ // Configurar indentación con tabulaciones
+ editor.Options.ConvertTabsToSpaces = false;
+ editor.Options.IndentationSize = 4;
+ }
+
+ private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
+ {
+ // Guardar configuración antes de cerrar
+ _viewModel.Settings.Save();
+ }
+
+ private void GridSplitter_DragCompleted(object sender, System.Windows.Controls.Primitives.DragCompletedEventArgs e)
+ {
+ // Actualizar anchos de columnas en el ViewModel
+ var columnDefinitions = EditorsGrid.ColumnDefinitions;
+ _viewModel.FirstColumnWidth = columnDefinitions[1].ActualWidth;
+ _viewModel.SecondColumnWidth = columnDefinitions[3].ActualWidth;
+ _viewModel.ThirdColumnWidth = columnDefinitions[5].ActualWidth;
+ }
+
+ private void TextEditor_MouseDoubleClick(object sender, MouseButtonEventArgs e)
+ {
+ // Seleccionar palabra completa al hacer doble clic
+ var editor = sender as TextEditor;
+ if (editor != null)
+ {
+ var position = editor.TextArea.Caret.Position;
+ var document = editor.Document;
+ var line = document.GetLineByOffset(position.Offset);
+ var lineText = document.GetText(line);
+
+ // Obtener palabra bajo el cursor
+ var offsetInLine = position.Offset - line.Offset;
+ int wordStart = offsetInLine;
+ int wordEnd = offsetInLine;
+
+ // Encontrar inicio de palabra
+ while (wordStart > 0 && IsWordPart(lineText[wordStart - 1]))
+ wordStart--;
+
+ // Encontrar fin de palabra
+ while (wordEnd < lineText.Length && IsWordPart(lineText[wordEnd]))
+ wordEnd++;
+
+ // Seleccionar palabra
+ if (wordEnd > wordStart)
+ {
+ editor.Select(line.Offset + wordStart, wordEnd - wordStart);
+ }
+ }
+ }
+
+ private bool IsWordPart(char c)
+ {
+ return char.IsLetterOrDigit(c) || c == '_';
+ }
+ }
+}
\ No newline at end of file
diff --git a/Settings.cs b/Settings.cs
new file mode 100644
index 0000000..c7771e9
--- /dev/null
+++ b/Settings.cs
@@ -0,0 +1,205 @@
+using System;
+using System.ComponentModel;
+using System.IO;
+using System.Runtime.CompilerServices;
+using System.Windows;
+using System.Xml.Serialization;
+
+namespace CodeMerger
+{
+ public class Settings : INotifyPropertyChanged
+ {
+ private static readonly string SettingsFilePath = Path.Combine(
+ Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
+ "CodeMerger",
+ "settings.xml");
+
+ #region Propiedades de Ventana
+
+ private double _windowWidth = 1200;
+ private double _windowHeight = 800;
+ private double _windowLeft = 100;
+ private double _windowTop = 100;
+ private WindowState _windowState = WindowState.Normal;
+
+ public double WindowWidth
+ {
+ get => _windowWidth;
+ set
+ {
+ if (_windowWidth != value)
+ {
+ _windowWidth = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public double WindowHeight
+ {
+ get => _windowHeight;
+ set
+ {
+ if (_windowHeight != value)
+ {
+ _windowHeight = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public double WindowLeft
+ {
+ get => _windowLeft;
+ set
+ {
+ if (_windowLeft != value)
+ {
+ _windowLeft = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public double WindowTop
+ {
+ get => _windowTop;
+ set
+ {
+ if (_windowTop != value)
+ {
+ _windowTop = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public WindowState WindowState
+ {
+ get => _windowState;
+ set
+ {
+ if (_windowState != value)
+ {
+ _windowState = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ #endregion
+
+ #region Tamaños de Columnas
+
+ private double _firstColumnWidth = 400;
+ private double _secondColumnWidth = 400;
+ private double _thirdColumnWidth = 400;
+
+ public double FirstColumnWidth
+ {
+ get => _firstColumnWidth;
+ set
+ {
+ if (_firstColumnWidth != value)
+ {
+ _firstColumnWidth = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public double SecondColumnWidth
+ {
+ get => _secondColumnWidth;
+ set
+ {
+ if (_secondColumnWidth != value)
+ {
+ _secondColumnWidth = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public double ThirdColumnWidth
+ {
+ get => _thirdColumnWidth;
+ set
+ {
+ if (_thirdColumnWidth != value)
+ {
+ _thirdColumnWidth = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ #endregion
+
+ #region Carga y Guardado
+
+ public static Settings Load()
+ {
+ try
+ {
+ // Asegurarse de que el directorio existe
+ string directory = Path.GetDirectoryName(SettingsFilePath);
+ if (!Directory.Exists(directory))
+ {
+ Directory.CreateDirectory(directory);
+ }
+
+ // Cargar configuración si existe
+ if (File.Exists(SettingsFilePath))
+ {
+ using (var stream = File.OpenRead(SettingsFilePath))
+ {
+ var serializer = new XmlSerializer(typeof(Settings));
+ return (Settings)serializer.Deserialize(stream);
+ }
+ }
+ }
+ catch (Exception)
+ {
+ // En caso de error, devolver configuración por defecto
+ }
+
+ return new Settings();
+ }
+
+ public void Save()
+ {
+ try
+ {
+ string directory = Path.GetDirectoryName(SettingsFilePath);
+ if (!Directory.Exists(directory))
+ {
+ Directory.CreateDirectory(directory);
+ }
+
+ using (var stream = File.Create(SettingsFilePath))
+ {
+ var serializer = new XmlSerializer(typeof(Settings));
+ serializer.Serialize(stream, this);
+ }
+ }
+ catch (Exception)
+ {
+ // Manejar error de guardado
+ }
+ }
+
+ #endregion
+
+ #region INotifyPropertyChanged
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/TestExamples/TestExampleCode.cs b/TestExamples/TestExampleCode.cs
new file mode 100644
index 0000000..0c97900
--- /dev/null
+++ b/TestExamples/TestExampleCode.cs
@@ -0,0 +1,396 @@
+namespace CodeMerger.TestExamples
+{
+ public static class TestExampleCode
+ {
+ // Ejemplo Simple
+ public static string SimpleMaster = @"
+public class SimpleClass
+{
+ private int _counter;
+
+ public int Counter
+ {
+ get { return _counter; }
+ set { _counter = value; }
+ }
+
+ public void Increment()
+ {
+ _counter++;
+ }
+}";
+
+ public static string SimpleSnippet = @"
+public void Decrement()
+{
+ _counter--;
+}";
+
+ // Manejo de Namespace
+ public static string NamespaceHandlingMaster = @"
+namespace TestNamespace
+{
+ public class MasterClass
+ {
+ public void ExistingMethod()
+ {
+ Console.WriteLine(""Original implementation"");
+ }
+ }
+}";
+
+ public static string NamespaceHandlingSnippet = @"
+namespace TestNamespace
+{
+ public class MasterClass
+ {
+ public void NewMethod()
+ {
+ Console.WriteLine(""New method"");
+ }
+ }
+}";
+
+ // Elementos Virtuales
+ public static string VirtualElementsMaster = @"
+// Sin namespace ni clase
+public int Calculate(int x, int y)
+{
+ return x + y;
+}";
+
+ public static string VirtualElementsSnippet = @"
+// Sin namespace ni clase
+public int Multiply(int x, int y)
+{
+ return x * y;
+}";
+
+ // Reemplazo de Métodos
+ public static string MethodReplacementMaster = @"
+namespace Calculator
+{
+ public class MathUtils
+ {
+ // Método original
+ public double CalculateAverage(int[] numbers)
+ {
+ int sum = 0;
+ foreach (var n in numbers)
+ sum += n;
+ return (double)sum / numbers.Length;
+ }
+ }
+}";
+
+ public static string MethodReplacementSnippet = @"
+// Mismo método con implementación mejorada
+public double CalculateAverage(int[] numbers)
+{
+ return numbers.Length > 0 ? numbers.Average() : 0;
+}";
+
+ // Merge Complejo
+ public static string ComplexMergeMaster = @"
+using System;
+using System.Collections.Generic;
+
+namespace DataAccess
+{
+ public class Repository where T : class
+ {
+ private List _items = new List();
+
+ public void Add(T item)
+ {
+ _items.Add(item);
+ }
+
+ public T GetById(int id)
+ {
+ // Implementación básica
+ return _items[id];
+ }
+
+ public IEnumerable GetAll()
+ {
+ return _items;
+ }
+ }
+}";
+
+ public static string ComplexMergeSnippet = @"
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace DataAccess
+{
+ public class Repository where T : class
+ {
+ // Método mejorado
+ public T GetById(int id)
+ {
+ // Implementación mejorada con validación
+ if (id < 0 || id >= _items.Count)
+ throw new ArgumentOutOfRangeException(nameof(id));
+
+ return _items[id];
+ }
+
+ // Método nuevo
+ public void Remove(T item)
+ {
+ _items.Remove(item);
+ }
+
+ // Método nuevo con sobrecarga
+ public void Remove(int id)
+ {
+ if (id < 0 || id >= _items.Count)
+ throw new ArgumentOutOfRangeException(nameof(id));
+
+ _items.RemoveAt(id);
+ }
+ }
+}";
+
+ // Múltiples Clases
+ public static string MultipleClassesMaster = @"
+using System;
+using System.Collections.Generic;
+
+namespace Demo
+{
+ public class Person
+ {
+ public string Name { get; set; }
+ public int Age { get; set; }
+
+ public override string ToString()
+ {
+ return $""{Name}, {Age} years old"";
+ }
+ }
+
+ public class Employee : Person
+ {
+ public string Department { get; set; }
+ public decimal Salary { get; set; }
+
+ public decimal CalculateBonus()
+ {
+ return Salary * 0.1m;
+ }
+ }
+}";
+
+ public static string MultipleClassesSnippet = @"
+using System;
+using System.Linq;
+
+namespace Demo
+{
+ // Modificar clase existente
+ public class Person
+ {
+ // Nuevo campo
+ private DateTime _birthDate;
+
+ // Nueva propiedad
+ public DateTime BirthDate
+ {
+ get { return _birthDate; }
+ set { _birthDate = value; Age = CalculateAge(value); }
+ }
+
+ // Nuevo método
+ private int CalculateAge(DateTime birthDate)
+ {
+ var today = DateTime.Today;
+ var age = today.Year - birthDate.Year;
+ if (birthDate > today.AddYears(-age)) age--;
+ return age;
+ }
+ }
+
+ // Nueva clase
+ public class Manager : Employee
+ {
+ public List Team { get; set; } = new List();
+
+ public decimal CalculateDepartmentSalary()
+ {
+ return Team.Sum(e => e.Salary) + Salary;
+ }
+
+ // Sobreescribir método
+ public new decimal CalculateBonus()
+ {
+ return Salary * 0.2m;
+ }
+ }
+}";
+
+ // Clases Anidadas
+ public static string NestedClassesMaster = @"
+using System;
+
+namespace Configuration
+{
+ public class ConfigManager
+ {
+ private static ConfigManager _instance;
+
+ public static ConfigManager Instance
+ {
+ get
+ {
+ if (_instance == null)
+ _instance = new ConfigManager();
+ return _instance;
+ }
+ }
+
+ public class ConfigSection
+ {
+ public string Name { get; set; }
+ public Dictionary Settings { get; set; } = new Dictionary();
+
+ public string GetSetting(string key)
+ {
+ return Settings.TryGetValue(key, out var value) ? value : null;
+ }
+ }
+
+ private Dictionary _sections = new Dictionary();
+
+ public ConfigSection GetSection(string name)
+ {
+ return _sections.TryGetValue(name, out var section) ? section : null;
+ }
+ }
+}";
+
+ public static string NestedClassesSnippet = @"
+namespace Configuration
+{
+ public class ConfigManager
+ {
+ // Clase anidada modificada
+ public class ConfigSection
+ {
+ // Nuevo método
+ public void SetSetting(string key, string value)
+ {
+ Settings[key] = value;
+ }
+
+ // Clase anidada dentro de otra clase anidada
+ public class EncryptedSetting
+ {
+ public string EncryptedValue { get; set; }
+ public string Decrypt()
+ {
+ // Simulación de desencriptación
+ return System.Text.Encoding.UTF8.GetString(
+ Convert.FromBase64String(EncryptedValue));
+ }
+ }
+ }
+
+ // Nuevo método
+ public void AddSection(string name)
+ {
+ if (!_sections.ContainsKey(name))
+ _sections[name] = new ConfigSection { Name = name };
+ }
+ }
+}";
+
+ // Comentarios y Formato
+ public static string CommentsFormatMaster = @"
+using System;
+
+namespace Documentation
+{
+ ///
+ /// Representa un artículo de documentación
+ ///
+ public class DocumentItem
+ {
+ // ID único del documento
+ public Guid Id { get; } = Guid.NewGuid();
+
+ ///
+ /// Título del documento
+ ///
+ public string Title { get; set; }
+
+ ///
+ /// Contenido del documento
+ ///
+ public string Content { get; set; }
+
+ ///
+ /// Fecha de creación
+ ///
+ public DateTime Created { get; set; } = DateTime.Now;
+
+ ///
+ /// Formatea el documento para visualización
+ ///
+ public string FormatDocument()
+ {
+ return $""# {Title}\n\n{Content}"";
+ }
+ }
+}";
+
+ public static string CommentsFormatSnippet = @"
+namespace Documentation
+{
+ public class DocumentItem
+ {
+ ///
+ /// Última fecha de modificación
+ ///
+ public DateTime LastModified { get; set; } = DateTime.Now;
+
+ ///
+ /// Etiquetas asociadas al documento
+ ///
+ public List Tags { get; set; } = new List();
+
+ ///
+ /// Verifica si el documento contiene una etiqueta específica
+ ///
+ /// Etiqueta a buscar
+ /// Verdadero si la etiqueta existe
+ public bool HasTag(string tag)
+ {
+ return Tags.Contains(tag, StringComparer.OrdinalIgnoreCase);
+ }
+
+ ///
+ /// Formatea el documento para visualización con metadatos
+ ///
+ public string FormatDocument()
+ {
+ var sb = new StringBuilder();
+ sb.AppendLine($""# {Title}"");
+ sb.AppendLine($""Created: {Created:yyyy-MM-dd}"");
+ sb.AppendLine($""Modified: {LastModified:yyyy-MM-dd}"");
+
+ if (Tags.Count > 0)
+ sb.AppendLine($""Tags: {string.Join(', ', Tags)}"");
+
+ sb.AppendLine();
+ sb.Append(Content);
+
+ return sb.ToString();
+ }
+ }
+}";
+ }
+}
\ No newline at end of file
diff --git a/ViewModels/MainWindowViewModel.cs b/ViewModels/MainWindowViewModel.cs
new file mode 100644
index 0000000..4b5e64c
--- /dev/null
+++ b/ViewModels/MainWindowViewModel.cs
@@ -0,0 +1,597 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+using CommunityToolkit.Mvvm.Input;
+using DocumentFormat.OpenXml.Wordprocessing;
+using ICSharpCode.AvalonEdit.Document;
+using Microsoft.Win32;
+
+namespace CodeMerger.ViewModels
+{
+ public class MainWindowViewModel : INotifyPropertyChanged
+ {
+ #region Campos Privados
+
+ private readonly cCodeMerger _codeMerger;
+ private readonly Settings _settings;
+ private string _searchText;
+ private double _firstColumnWidth;
+ private double _secondColumnWidth;
+ private double _thirdColumnWidth;
+ private TextDocument _originalDocument;
+ private TextDocument _llmDocument;
+ private TextDocument _mergedDocument;
+ private CodeElement _originalStructure;
+ private CodeElement _llmStructure;
+ private CodeElement _mergedStructure;
+ private ObservableCollection _originalNamespaces;
+ private ObservableCollection _originalClasses;
+ private string _selectedLLMNamespace;
+ private string _selectedLLMClass;
+
+ #endregion
+
+ #region Constructores
+
+ public MainWindowViewModel()
+ {
+ // Cargar configuración
+ _settings = Settings.Load();
+
+ // Inicializar CodeMerger
+ _codeMerger = new cCodeMerger();
+
+ // Inicializar documentos
+ _originalDocument = new TextDocument();
+ _llmDocument = new TextDocument();
+ _mergedDocument = new TextDocument();
+
+ // Inicializar anchos de columnas
+ _firstColumnWidth = _settings.FirstColumnWidth;
+ _secondColumnWidth = _settings.SecondColumnWidth;
+ _thirdColumnWidth = _settings.ThirdColumnWidth;
+
+ // Inicializar colecciones
+ _originalNamespaces = new ObservableCollection();
+ _originalClasses = new ObservableCollection();
+
+ // Inicializar comandos
+ InitializeCommands();
+ }
+
+ #endregion
+
+ #region Propiedades
+
+ public Settings Settings => _settings;
+
+ public string SearchText
+ {
+ get => _searchText;
+ set
+ {
+ if (_searchText != value)
+ {
+ _searchText = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public double FirstColumnWidth
+ {
+ get => _firstColumnWidth;
+ set
+ {
+ if (_firstColumnWidth != value)
+ {
+ _firstColumnWidth = value;
+ _settings.FirstColumnWidth = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public double SecondColumnWidth
+ {
+ get => _secondColumnWidth;
+ set
+ {
+ if (_secondColumnWidth != value)
+ {
+ _secondColumnWidth = value;
+ _settings.SecondColumnWidth = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public double ThirdColumnWidth
+ {
+ get => _thirdColumnWidth;
+ set
+ {
+ if (_thirdColumnWidth != value)
+ {
+ _thirdColumnWidth = value;
+ _settings.ThirdColumnWidth = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public TextDocument OriginalDocument
+ {
+ get => _originalDocument;
+ set
+ {
+ if (_originalDocument != value)
+ {
+ _originalDocument = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public TextDocument LLMDocument
+ {
+ get => _llmDocument;
+ set
+ {
+ if (_llmDocument != value)
+ {
+ _llmDocument = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public TextDocument MergedDocument
+ {
+ get => _mergedDocument;
+ set
+ {
+ if (_mergedDocument != value)
+ {
+ _mergedDocument = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public CodeElement OriginalStructure
+ {
+ get => _originalStructure;
+ set
+ {
+ if (_originalStructure != value)
+ {
+ _originalStructure = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public CodeElement LLMStructure
+ {
+ get => _llmStructure;
+ set
+ {
+ if (_llmStructure != value)
+ {
+ _llmStructure = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public CodeElement MergedStructure
+ {
+ get => _mergedStructure;
+ set
+ {
+ if (_mergedStructure != value)
+ {
+ _mergedStructure = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public ObservableCollection OriginalNamespaces
+ {
+ get => _originalNamespaces;
+ set
+ {
+ if (_originalNamespaces != value)
+ {
+ _originalNamespaces = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public ObservableCollection OriginalClasses
+ {
+ get => _originalClasses;
+ set
+ {
+ if (_originalClasses != value)
+ {
+ _originalClasses = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public string SelectedLLMNamespace
+ {
+ get => _selectedLLMNamespace;
+ set
+ {
+ if (_selectedLLMNamespace != value)
+ {
+ _selectedLLMNamespace = value;
+ OnPropertyChanged();
+
+ // Actualizar namespace por defecto en CodeMerger
+ if (!string.IsNullOrEmpty(value))
+ _codeMerger.DefaultNamespace = value;
+ }
+ }
+ }
+
+ public string SelectedLLMClass
+ {
+ get => _selectedLLMClass;
+ set
+ {
+ if (_selectedLLMClass != value)
+ {
+ _selectedLLMClass = value;
+ OnPropertyChanged();
+
+ // Actualizar clase por defecto en CodeMerger
+ if (!string.IsNullOrEmpty(value))
+ _codeMerger.DefaultClass = value;
+ }
+ }
+ }
+
+ #endregion
+
+ #region Comandos
+
+ public ICommand PasteOriginalCommand { get; private set; }
+ public ICommand CopyOriginalCommand { get; private set; }
+ public ICommand PasteLLMCommand { get; private set; }
+ public ICommand CopyLLMCommand { get; private set; }
+ public ICommand CopyMergedCommand { get; private set; }
+ public ICommand SearchCommand { get; private set; }
+ public ICommand ClearAllCommand { get; private set; }
+ public ICommand AnalyzeCodeCommand { get; private set; }
+ public ICommand MergeCodeCommand { get; private set; }
+ public ICommand ShowOriginalStructureCommand { get; private set; }
+ public ICommand ShowLLMStructureCommand { get; private set; }
+ public ICommand ShowMergedStructureCommand { get; private set; }
+
+ private void InitializeCommands()
+ {
+ PasteOriginalCommand = new RelayCommand(PasteOriginal);
+ CopyOriginalCommand = new RelayCommand(CopyOriginal);
+ PasteLLMCommand = new RelayCommand(PasteLLM);
+ CopyLLMCommand = new RelayCommand(CopyLLM);
+ CopyMergedCommand = new RelayCommand(CopyMerged);
+ SearchCommand = new RelayCommand(Search);
+ ClearAllCommand = new RelayCommand(ClearAll);
+ AnalyzeCodeCommand = new RelayCommand(AnalyzeCode);
+ MergeCodeCommand = new RelayCommand(MergeCode);
+ ShowOriginalStructureCommand = new RelayCommand(ShowOriginalStructure);
+ ShowLLMStructureCommand = new RelayCommand(ShowLLMStructure);
+ ShowMergedStructureCommand = new RelayCommand(ShowMergedStructure);
+ }
+
+ #endregion
+
+ #region Métodos de Comando
+
+ private void PasteOriginal()
+ {
+ if (Clipboard.ContainsText())
+ {
+ OriginalDocument.Text = Clipboard.GetText();
+ LogInfo("Código original pegado desde el portapapeles");
+ }
+ }
+
+ private void CopyOriginal()
+ {
+ if (!string.IsNullOrEmpty(OriginalDocument.Text))
+ {
+ Clipboard.SetText(OriginalDocument.Text);
+ LogInfo("Código original copiado al portapapeles");
+ }
+ }
+
+ private void PasteLLM()
+ {
+ if (Clipboard.ContainsText())
+ {
+ LLMDocument.Text = Clipboard.GetText();
+ LogInfo("Código LLM pegado desde el portapapeles");
+ }
+ }
+
+ private void CopyLLM()
+ {
+ if (!string.IsNullOrEmpty(LLMDocument.Text))
+ {
+ Clipboard.SetText(LLMDocument.Text);
+ LogInfo("Código LLM copiado al portapapeles");
+ }
+ }
+
+ private void CopyMerged()
+ {
+ if (!string.IsNullOrEmpty(MergedDocument.Text))
+ {
+ Clipboard.SetText(MergedDocument.Text);
+ LogInfo("Código fusionado copiado al portapapeles");
+ }
+ }
+
+ private void Search()
+ {
+ if (string.IsNullOrEmpty(SearchText))
+ return;
+
+ MessageBox.Show($"Función de búsqueda por implementar: {SearchText}");
+ }
+
+ private void ClearAll()
+ {
+ OriginalDocument.Text = string.Empty;
+ LLMDocument.Text = string.Empty;
+ MergedDocument.Text = string.Empty;
+ OriginalStructure = null;
+ LLMStructure = null;
+ MergedStructure = null;
+ LogInfo("Todos los editores han sido limpiados");
+ }
+
+ private void AnalyzeCode()
+ {
+ try
+ {
+ // Analizar código original
+ if (!string.IsNullOrEmpty(OriginalDocument.Text))
+ {
+ OriginalStructure = _codeMerger.AnalyzeCode(OriginalDocument.Text);
+ LogInfo("Código original analizado correctamente");
+
+ // Extraer namespaces y clases para las listas desplegables
+ UpdateNamespacesAndClasses();
+ }
+
+ // Analizar código LLM
+ if (!string.IsNullOrEmpty(LLMDocument.Text))
+ {
+ LLMStructure = _codeMerger.AnalyzeCode(LLMDocument.Text);
+ LogInfo("Código LLM analizado correctamente");
+ }
+
+ // Analizar código fusionado (si existe)
+ if (!string.IsNullOrEmpty(MergedDocument.Text))
+ {
+ MergedStructure = _codeMerger.AnalyzeCode(MergedDocument.Text);
+ LogInfo("Código fusionado analizado correctamente");
+ }
+ }
+ catch (Exception ex)
+ {
+ LogError($"Error al analizar código: {ex.Message}");
+ MessageBox.Show($"Error al analizar código: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ }
+
+ private void MergeCode()
+ {
+ try
+ {
+ if (string.IsNullOrEmpty(OriginalDocument.Text))
+ {
+ LogWarning("El código original está vacío");
+ return;
+ }
+
+ if (string.IsNullOrEmpty(LLMDocument.Text))
+ {
+ LogWarning("El código LLM está vacío");
+ return;
+ }
+
+ // Realizar el merge
+ string mergedCode = _codeMerger.MergeCode(OriginalDocument.Text, LLMDocument.Text);
+ MergedDocument.Text = mergedCode;
+
+ // Analizar el código fusionado
+ MergedStructure = _codeMerger.AnalyzeCode(mergedCode);
+
+ // Mostrar log del merge
+ string mergeLog = _codeMerger.GetLog();
+ LogInfo("Código fusionado generado correctamente");
+ LogMergeDetails(mergeLog);
+ }
+ catch (Exception ex)
+ {
+ LogError($"Error al fusionar código: {ex.Message}");
+ MessageBox.Show($"Error al fusionar código: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ }
+
+ private void ShowOriginalStructure()
+ {
+ if (OriginalStructure == null)
+ {
+ if (!string.IsNullOrEmpty(OriginalDocument.Text))
+ {
+ OriginalStructure = _codeMerger.AnalyzeCode(OriginalDocument.Text);
+ }
+ else
+ {
+ LogWarning("No hay código original para mostrar estructura");
+ return;
+ }
+ }
+
+ ShowStructureWindow("Estructura del Código Original", OriginalStructure);
+ }
+
+ private void ShowLLMStructure()
+ {
+ if (LLMStructure == null)
+ {
+ if (!string.IsNullOrEmpty(LLMDocument.Text))
+ {
+ LLMStructure = _codeMerger.AnalyzeCode(LLMDocument.Text);
+ }
+ else
+ {
+ LogWarning("No hay código LLM para mostrar estructura");
+ return;
+ }
+ }
+
+ ShowStructureWindow("Estructura del Código LLM", LLMStructure);
+ }
+
+ private void ShowMergedStructure()
+ {
+ if (MergedStructure == null)
+ {
+ if (!string.IsNullOrEmpty(MergedDocument.Text))
+ {
+ MergedStructure = _codeMerger.AnalyzeCode(MergedDocument.Text);
+ }
+ else
+ {
+ LogWarning("No hay código fusionado para mostrar estructura");
+ return;
+ }
+ }
+
+ ShowStructureWindow("Estructura del Código Fusionado", MergedStructure);
+ }
+
+ #endregion
+
+ #region Métodos Auxiliares
+
+ private void UpdateNamespacesAndClasses()
+ {
+ OriginalNamespaces.Clear();
+ OriginalClasses.Clear();
+
+ if (OriginalStructure != null)
+ {
+ // Extraer namespaces
+ var namespaces = OriginalStructure.GetAllDescendantsOfType(CodeElement.ElementType.Namespace)
+ .Where(n => !n.IsVirtualNamespace)
+ .Select(n => n.Name)
+ .ToList();
+
+ foreach (var ns in namespaces)
+ {
+ OriginalNamespaces.Add(ns);
+ }
+
+ // Extraer clases
+ var classes = OriginalStructure.GetAllDescendantsOfType(CodeElement.ElementType.Class)
+ .Where(c => !c.IsVirtualClass)
+ .Select(c => c.Name)
+ .ToList();
+
+ foreach (var cls in classes)
+ {
+ OriginalClasses.Add(cls);
+ }
+
+ // Seleccionar valores por defecto si hay opciones disponibles
+ if (OriginalNamespaces.Count > 0 && string.IsNullOrEmpty(SelectedLLMNamespace))
+ {
+ SelectedLLMNamespace = OriginalNamespaces[0];
+ }
+
+ if (OriginalClasses.Count > 0 && string.IsNullOrEmpty(SelectedLLMClass))
+ {
+ SelectedLLMClass = OriginalClasses[0];
+ }
+ }
+ }
+
+ private void ShowStructureWindow(string title, CodeElement structure)
+ {
+ var structureWindow = new Controls.CodeStructureTreeView();
+ structureWindow.Title = title;
+ structureWindow.LoadCodeStructure(structure);
+ structureWindow.Owner = Application.Current.MainWindow;
+ structureWindow.Show();
+ }
+
+ private void LogInfo(string message)
+ {
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ var logControl = (Application.Current.MainWindow as MainWindow)?.logControl;
+ logControl?.LogInfo(message);
+ });
+ }
+
+ private void LogWarning(string message)
+ {
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ var logControl = (Application.Current.MainWindow as MainWindow)?.logControl;
+ logControl?.LogWarning(message);
+ });
+ }
+
+ private void LogError(string message)
+ {
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ var logControl = (Application.Current.MainWindow as MainWindow)?.logControl;
+ logControl?.LogError(message);
+ });
+ }
+
+ private void LogMergeDetails(string mergeLog)
+ {
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ var logControl = (Application.Current.MainWindow as MainWindow)?.logControl;
+ logControl?.LogMergeDetails(mergeLog);
+ });
+ }
+
+ #endregion
+
+ #region INotifyPropertyChanged
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file