мини рефакторинг

добавлены пачноты
This commit is contained in:
2026-07-24 14:07:34 +04:00
parent eb8dac7627
commit ae1d7a12b8
9 changed files with 31 additions and 408 deletions

View File

@@ -1,190 +0,0 @@
using CodeBase.Models;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.MSBuild;
namespace CodeBase.Services
{
public class ChunkService(VectorizationService vectorizationService)
{
public List<CodeChunk> ChunkCSharpFile(string filePath, string fileContent)
{
var chunks = new List<CodeChunk>();
var syntaxTree = CSharpSyntaxTree.ParseText(fileContent);
var root = syntaxTree.GetRoot();
var compilation = CSharpCompilation.Create("MyAnalysis")
.AddSyntaxTrees(syntaxTree)
// Подкидываем базовые библиотеки .NET, чтобы он узнал System.Linq и прочее
.AddReferences(MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location));
var semanticModel = compilation.GetSemanticModel(syntaxTree);
// 1. Собираем методы (как и раньше)
var methods = root.DescendantNodes().OfType<MethodDeclarationSyntax>();
foreach (var method in methods)
{
var chunk = new CodeChunk
{
FilePath = filePath,
EntityName = method.Identifier.Text,
Content = method.ToFullString().Trim()
};
var invocations = method.DescendantNodes().OfType<InvocationExpressionSyntax>();
foreach (var invocation in invocations)
{
var symbolInfo = semanticModel.GetSymbolInfo(invocation);
if (symbolInfo.Symbol is IMethodSymbol methodSymbol)
{
// Получаем полный путь пространства имен (например, "System.Linq")
string namespaceName = methodSymbol.ContainingNamespace.ToString();
// Пропускаем все системные вызовы .NET
if (namespaceName.StartsWith("System") || namespaceName.StartsWith("Microsoft"))
{
continue;
}
// Если это наш метод (например, "Bas.Core.Services"), добавляем его
chunk.OutgoingCalls.Add(methodSymbol.Name);
Console.WriteLine($"[ПАРСЕР] Добавлена бизнес-связь -> {methodSymbol.Name}");
}
}
chunk.OutgoingCalls = chunk.OutgoingCalls.Distinct().ToList();
chunks.Add(chunk);
}
// 2. ДОБАВЛЯЕМ СБОР ENUM (Перечислений)
var enums = root.DescendantNodes().OfType<EnumDeclarationSyntax>();
foreach (var enumSyntax in enums)
{
chunks.Add(new CodeChunk
{
FilePath = filePath,
EntityName = enumSyntax.Identifier.Text, // Сохраняем имя енама
Content = enumSyntax.ToFullString().Trim()
});
}
// 3. ДОБАВЛЯЕМ СБОР КЛАССОВ-МОДЕЛЕЙ (без методов)
var classes = root.DescendantNodes().OfType<ClassDeclarationSyntax>();
foreach (var classSyntax in classes)
{
// Берем только свойства, чтобы понимать структуру модели
var properties = classSyntax.Members.OfType<PropertyDeclarationSyntax>();
if (properties.Any())
{
chunks.Add(new CodeChunk
{
FilePath = filePath,
EntityName = classSyntax.Identifier.Text,
// Сохраняем объявление класса и его свойства
Content = $"class {classSyntax.Identifier.Text} {{\n" +
string.Join("\n", properties.Select(p => p.ToFullString().Trim())) +
"\n}"
});
}
}
return chunks;
}
public async Task<List<CodeChunk>> ParseProjectAsync(string csprojPath)
{
// 1. Создаем воркспейс и загружаем проект (это может занять пару секунд)
using var workspace = MSBuildWorkspace.Create();
Console.WriteLine("Загружаем проект и строим семантическую модель...");
var project = await workspace.OpenProjectAsync(csprojPath);
var chunksList = new List<CodeChunk>();
// 2. Проходимся по всем C#-файлам в проекте
foreach (var document in project.Documents)
{
// Теперь у нас есть ГАРАНТИРОВАННАЯ семантическая модель для каждого файла
var semanticModel = await document.GetSemanticModelAsync();
var syntaxTree = await document.GetSyntaxTreeAsync();
var root = await syntaxTree.GetRootAsync();
var methods = root.DescendantNodes().OfType<MethodDeclarationSyntax>();
foreach (var method in methods)
{
var chunk = new CodeChunk
{
EntityName = method.Identifier.Text,
Content = method.ToFullString(),
FilePath = document.FilePath
};
var invocations = method.DescendantNodes().OfType<InvocationExpressionSyntax>();
foreach (var invocation in invocations)
{
// 3. Просим у модели 100% точную информацию о вызываемом методе
var symbolInfo = semanticModel.GetSymbolInfo(invocation);
if (symbolInfo.Symbol is IMethodSymbol methodSymbol)
{
// Получаем пространство имен метода (например, "System.Linq" или "CodeBase.Services")
string namespaceName = methodSymbol.ContainingNamespace?.ToString() ?? "";
// Отсеиваем только системные вызовы, оставляя ВЕСЬ бизнес-код
if (!namespaceName.StartsWith("System") && !namespaceName.StartsWith("Microsoft"))
{
chunk.OutgoingCalls.Add(methodSymbol.Name);
Console.WriteLine($"[ПАРСЕР] Успех: {chunk.EntityName} -> {methodSymbol.Name} ({namespaceName})");
}
}
}
// 1. Ищем вообще ВСЕ слова (идентификаторы) внутри метода
var identifiers = method.DescendantNodes().OfType<IdentifierNameSyntax>();
foreach (var identifier in identifiers)
{
// 2. Спрашиваем у семантической модели: "Что это за слово?"
var symbolInfo = semanticModel.GetSymbolInfo(identifier);
// 3. Если это тип данных (класс, структура, интерфейс или ЕНУМ)
if (symbolInfo.Symbol is INamedTypeSymbol typeSymbol)
{
string namespaceName = typeSymbol.ContainingNamespace?.ToString() ?? "";
// Отсеиваем системные типы (string, int, List и т.д.)
if (!namespaceName.StartsWith("System") && !namespaceName.StartsWith("Microsoft"))
{
// Проверяем, что это именно то, что нам нужно
if (typeSymbol.TypeKind == TypeKind.Enum)
{
chunk.OutgoingCalls.Add(typeSymbol.Name);
Console.WriteLine($"[ПАРСЕР] Нашли использование енума -> {typeSymbol.Name}");
}
else if (typeSymbol.TypeKind == TypeKind.Class || typeSymbol.TypeKind == TypeKind.Interface)
{
chunk.OutgoingCalls.Add(typeSymbol.Name);
Console.WriteLine($"[ПАРСЕР] Нашли использование класса/интерфейса -> {typeSymbol.Name}");
}
}
}
}
// Убираем дубликаты
chunk.OutgoingCalls = chunk.OutgoingCalls.Distinct().ToList();
chunksList.Add(chunk);
}
}
return chunksList;
}
}
}

View File

@@ -3,27 +3,10 @@ using CodeBase.Repositories;
namespace CodeBase.Services
{
public class CodeService(ChunkService service,
ScipRepository repository,
public class CodeService(ScipRepository repository,
VectorizationService vectorizationService,
LlmService llmService)
{
public async Task<List<CodeChunk>> GetCodeChunksAsync(string path, string name)
{
if (!Path.Exists(path))
{
throw new Exception("Путь не найден");
}
var allChunks = await service.ParseProjectAsync(path);
var chunks = await vectorizationService.EnrichChunksWithVectorsAsync(allChunks);
//await repository.SaveChunksAsync(name, chunks);
return chunks;
}
public async Task<string> GetAnswerAsync(string name, string question)
{
var query = await vectorizationService.GetVectorAsync(question);
@@ -45,29 +28,5 @@ namespace CodeBase.Services
return results;
}
// Математика косинусного сходства
private float CalculateCosineSimilarity(float[] vectorA, float[] vectorB)
{
if (vectorA.Length != vectorB.Length)
throw new ArgumentException("Векторы должны быть одинаковой длины (например, 768).");
float dotProduct = 0;
float magnitudeA = 0;
float magnitudeB = 0;
for (int i = 0; i < vectorA.Length; i++)
{
dotProduct += vectorA[i] * vectorB[i];
magnitudeA += vectorA[i] * vectorA[i];
magnitudeB += vectorB[i] * vectorB[i];
}
// Защита от деления на ноль
if (magnitudeA == 0 || magnitudeB == 0)
return 0;
return (float)(dotProduct / (Math.Sqrt(magnitudeA) * Math.Sqrt(magnitudeB)));
}
}
}