diff --git a/CodeContextGenerator/CodeContextGenerator.csproj b/CodeContextGenerator/CodeContextGenerator.csproj
index cdf319c..d9b5680 100644
--- a/CodeContextGenerator/CodeContextGenerator.csproj
+++ b/CodeContextGenerator/CodeContextGenerator.csproj
@@ -8,6 +8,10 @@
true
+
+
+
+
True
diff --git a/CodeContextGenerator/Models/FileItem.cs b/CodeContextGenerator/Models/FileItem.cs
index a076661..f213d2f 100644
--- a/CodeContextGenerator/Models/FileItem.cs
+++ b/CodeContextGenerator/Models/FileItem.cs
@@ -1,77 +1,66 @@
-using System.Collections.ObjectModel;
-using System.ComponentModel;
-using System.Runtime.CompilerServices;
+using CommunityToolkit.Mvvm.ComponentModel;
+using System.Collections.ObjectModel;
namespace CodeContextGenerator.Models
{
- public class FileItem : INotifyPropertyChanged
+ public partial class FileItem : ObservableObject
{
- public string Name { get; set; }
- public string FullName { get; set; }
- public bool IsDirectory { get; set; }
- public ObservableCollection Children { get; set; } = new ObservableCollection();
- public FileItem Parent { get; set; }
+ [ObservableProperty]
+ private string? name;
- private bool? _isSelected;
- public bool? IsSelected
+ [ObservableProperty]
+ private string? fullName;
+
+ [ObservableProperty]
+ private bool isDirectory;
+
+ [ObservableProperty]
+ private bool? isSelected;
+
+ [ObservableProperty]
+ private FileItem? parent;
+
+ public ObservableCollection Children { get; } = new ObservableCollection();
+
+ /*partial void OnIsSelectedChanged(bool? oldValue, bool? newValue)
{
- get => _isSelected;
- set
+ if (newValue.HasValue)
{
- if (_isSelected != value)
- {
- _isSelected = value;
- OnPropertyChanged();
- UpdateParentSelection();
-
- // Если это директория и установлено конкретное значение (не null), применяем ко всем детям
- if (IsDirectory && value.HasValue)
- {
- foreach (var child in Children)
- {
- child.IsSelected = value.Value;
- }
- }
- }
+ UpdateChildrenSelection(newValue.Value);
}
- }
+ UpdateParentSelection();
+ }*/
- public event PropertyChangedEventHandler? PropertyChanged;
-
- protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
+ private void UpdateChildrenSelection(bool value)
{
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ if (!IsDirectory) return;
+
+ foreach (var child in Children)
+ {
+ child.IsSelected = value;
+ }
}
private void UpdateParentSelection()
{
if (Parent == null) return;
- var children = Parent.Children.ToList();
+ var children = Parent.Children;
var allSelected = children.All(c => c.IsSelected == true);
var noneSelected = children.All(c => c.IsSelected == false);
+ var hasIndeterminate = children.Any(c => c.IsSelected == null);
- // Если есть дети с null - устанавливаем null
- bool hasIndeterminate = children.Any(c => c.IsSelected == null);
-
- if (hasIndeterminate)
- {
- Parent.IsSelected = null;
- }
- else if (allSelected)
- {
- Parent.IsSelected = true;
- }
- else if (noneSelected)
- {
- Parent.IsSelected = false;
- }
- else
- {
- Parent.IsSelected = null;
- }
-
+ Parent.IsSelected = hasIndeterminate ? null : (allSelected ? true : (noneSelected ? false : null));
Parent.UpdateParentSelection();
}
+
+ public void ClearSelection()
+ {
+ IsSelected = false;
+ foreach (var child in Children)
+ {
+ child.ClearSelection();
+ }
+ }
}
}
\ No newline at end of file
diff --git a/CodeContextGenerator/ViewModels/MainViewModel.cs b/CodeContextGenerator/ViewModels/MainViewModel.cs
index d95ec48..f585294 100644
--- a/CodeContextGenerator/ViewModels/MainViewModel.cs
+++ b/CodeContextGenerator/ViewModels/MainViewModel.cs
@@ -1,522 +1,431 @@
-using System.ComponentModel;
-using System.Runtime.CompilerServices;
-using System.Windows;
-using System.Windows.Input;
-using Microsoft.Win32;
-using CodeContextGenerator.Models;
+using CodeContextGenerator.Models;
using CodeContextGenerator.Services;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using Microsoft.Win32;
+using System.ComponentModel;
using System.IO;
using System.Text;
-using System.Threading.Tasks;
-using System.Threading;
-using System.Collections.Generic;
-using System.Linq;
+using System.Windows;
-namespace CodeContextGenerator.ViewModels
+namespace CodeContextGenerator.ViewModels;
+
+public partial class MainViewModel : ObservableObject, INotifyPropertyChanged
{
- public class MainViewModel : INotifyPropertyChanged
+ [ObservableProperty]
+ private FileItem? _rootDirectory = new();
+ [ObservableProperty]
+ private string _selectedProjectFilePath = string.Empty;
+ [ObservableProperty]
+ private bool _isProcessing = false;
+ [ObservableProperty]
+ private int _progressValue;
+ [ObservableProperty]
+ private string _progressText = string.Empty;
+ [ObservableProperty]
+ private string? _lastProjectPath = string.Empty;
+ [ObservableProperty]
+ private bool _isProjectLoaded;
+
+ private CancellationTokenSource _cancellationTokenSource = new();
+
+ public bool CanSelectFolder => !IsProcessing;
+ public bool CanGenerate => !IsProcessing && IsProjectLoaded && HasSelectedFiles(RootDirectory);
+
+ //public ICommand SelectProjectFileCommand { get; }
+ //public ICommand GenerateCommand { get; }
+ //public ICommand CancelCommand { get; }
+ //public ICommand ExitCommand { get; }
+
+ public MainViewModel()
{
- private FileItem _rootDirectory;
- private string _selectedProjectFilePath;
- private bool _isProcessing;
- private int _progressValue;
- private string _progressText;
- private CancellationTokenSource _cancellationTokenSource;
- private string _lastProjectPath;
- private bool _isProjectLoaded;
+ //ExitCommand = new RelayCommand(_ => Application.Current.Shutdown());
+ LoadSettings();
+ }
- public FileItem RootDirectory
+ private bool HasSelectedFiles(FileItem item)
+ {
+ if (item == null) return false;
+ if (item.IsSelected == true && !item.IsDirectory) return true;
+ return item.Children.Any(HasSelectedFiles);
+ }
+
+ //[RelayCommand(CanExecute = nameof(CanSelectFolder))]
+ [RelayCommand(CanExecute = nameof(CanSelectFolder))]
+ private void Exit()
+ {
+ Application.Current.Shutdown();
+ }
+
+ //[RelayCommand(CanExecute = nameof(CanSelectFolder))]
+ [RelayCommand(CanExecute = nameof(CanGenerate))]
+ private void Cancel()
+ {
+ CancelProcessing();
+ }
+
+ [RelayCommand(CanExecute = nameof(CanSelectFolder))]
+ private async Task SelectProjectFileAsync()
+ {
+ var dialog = new OpenFileDialog
{
- get => _rootDirectory;
- set
+ Title = "Выберите файл решения или проекта",
+ CheckFileExists = true,
+ CheckPathExists = true,
+ Filter = "Файлы решений (*.sln)|*.sln|Файлы проектов (*.csproj)|*.csproj|Все поддерживаемые (*.sln;*.csproj)|*.sln;*.csproj|Все файлы (*.*)|*.*",
+ InitialDirectory = !string.IsNullOrEmpty(LastProjectPath) && Directory.Exists(LastProjectPath) ? LastProjectPath : null
+ };
+
+ if (dialog.ShowDialog() != true) return;
+
+ SelectedProjectFilePath = dialog.FileName;
+ LastProjectPath = Path.GetDirectoryName(SelectedProjectFilePath);
+ SaveSettings();
+
+ IsProjectLoaded = false;
+ await LoadProjectAsync(SelectedProjectFilePath);
+ }
+
+ private async Task LoadProjectAsync(string projectFilePath)
+ {
+ IsProcessing = true;
+ ProgressText = "Загрузка проекта...";
+ ProgressValue = 0;
+
+ try
+ {
+ string? projectDirectory = Path.GetDirectoryName(projectFilePath);
+ var progress = new Progress(value =>
{
- _rootDirectory = value;
- OnPropertyChanged();
+ ProgressValue = value;
+ ProgressText = $"Загрузка: {value}%";
+ });
+
+ _cancellationTokenSource = new CancellationTokenSource();
+
+ if (projectFilePath.EndsWith(".sln", StringComparison.OrdinalIgnoreCase))
+ {
+ await LoadSolutionAsync(projectFilePath, progress, _cancellationTokenSource.Token);
}
- }
-
- public string SelectedProjectFilePath
- {
- get => _selectedProjectFilePath;
- set
+ else if (projectFilePath.EndsWith(".csproj", StringComparison.OrdinalIgnoreCase))
{
- _selectedProjectFilePath = value;
- OnPropertyChanged();
- }
- }
-
- public bool IsProcessing
- {
- get => _isProcessing;
- set
- {
- _isProcessing = value;
- OnPropertyChanged();
- OnPropertyChanged(nameof(CanSelectFolder));
- OnPropertyChanged(nameof(CanGenerate));
- }
- }
-
- public int ProgressValue
- {
- get => _progressValue;
- set
- {
- _progressValue = value;
- OnPropertyChanged();
- }
- }
-
- public string ProgressText
- {
- get => _progressText;
- set
- {
- _progressText = value;
- OnPropertyChanged();
- }
- }
-
- public bool IsProjectLoaded
- {
- get => _isProjectLoaded;
- set
- {
- _isProjectLoaded = value;
- OnPropertyChanged();
- }
- }
-
- public bool CanSelectFolder => !IsProcessing;
- public bool CanGenerate => !IsProcessing && IsProjectLoaded && HasSelectedFiles(RootDirectory);
-
- public ICommand SelectProjectFileCommand { get; }
- public ICommand GenerateCommand { get; }
- public ICommand CancelCommand { get; }
- public ICommand ExitCommand { get; }
-
- public MainViewModel()
- {
- SelectProjectFileCommand = new RelayCommand(SelectProjectFileAsync);
- GenerateCommand = new RelayCommand(GenerateFileAsync, _ => CanGenerate);
- CancelCommand = new RelayCommand(CancelProcessing, _ => IsProcessing);
- ExitCommand = new RelayCommand(_ => Application.Current.Shutdown());
-
- LoadSettings();
- }
-
- private bool HasSelectedFiles(FileItem item)
- {
- if (item == null) return false;
-
- if (item.IsSelected == true && !item.IsDirectory)
- return true;
-
- foreach (var child in item.Children)
- {
- if (HasSelectedFiles(child))
- return true;
+ await LoadProjectDirectoryAsync(projectDirectory, progress, _cancellationTokenSource.Token);
}
- return false;
+ IsProjectLoaded = true;
+ ProgressText = "Проект загружен успешно";
}
-
- private async void SelectProjectFileAsync(object parameter)
+ catch (OperationCanceledException)
{
- if (!CanSelectFolder) return;
+ ProgressText = "Загрузка отменена";
+ RootDirectory = null;
+ IsProjectLoaded = false;
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show($"Ошибка при загрузке проекта: {ex.Message}", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
+ RootDirectory = null;
+ IsProjectLoaded = false;
+ }
+ finally
+ {
+ IsProcessing = false;
+ }
+ }
- var dialog = new OpenFileDialog
+ private async Task LoadSolutionAsync(string solutionPath, IProgress progress, CancellationToken cancellationToken)
+ {
+ try
+ {
+ string? solutionDirectory = Path.GetDirectoryName(solutionPath);
+ var solutionProjects = ParseSolutionProjects(solutionPath);
+
+ var solutionItem = new FileItem
{
- Title = "Выберите файл решения или проекта",
- CheckFileExists = true,
- CheckPathExists = true,
- Filter = "Файлы решений Visual Studio (*.sln)|*.sln|Файлы проектов C# (*.csproj)|*.csproj|Все поддерживаемые файлы (*.sln;*.csproj)|*.sln;*.csproj|Все файлы (*.*)|*.*",
- Multiselect = false
+ Name = Path.GetFileName(solutionPath),
+ FullName = solutionPath,
+ IsDirectory = true,
+ IsSelected = false
};
- if (!string.IsNullOrEmpty(_lastProjectPath) && Directory.Exists(_lastProjectPath))
- {
- dialog.InitialDirectory = _lastProjectPath;
- }
+ int totalProjects = solutionProjects.Count;
+ int processedProjects = 0;
- bool? result = dialog.ShowDialog();
-
- if (result == true)
- {
- string selectedFilePath = dialog.FileName;
- string projectDirectory = Path.GetDirectoryName(selectedFilePath);
-
- if (!string.IsNullOrEmpty(projectDirectory))
- {
- _lastProjectPath = projectDirectory;
- SaveSettings();
-
- SelectedProjectFilePath = selectedFilePath;
- IsProjectLoaded = false;
-
- await LoadProjectAsync(selectedFilePath);
- }
- }
- }
-
- private async Task LoadProjectAsync(string projectFilePath)
- {
- IsProcessing = true;
- ProgressText = "Загрузка проекта...";
- ProgressValue = 0;
-
- try
- {
- string projectDirectory = Path.GetDirectoryName(projectFilePath);
- SelectedProjectFilePath = projectFilePath;
-
- var progress = new Progress(value =>
- {
- ProgressValue = value;
- ProgressText = $"Загрузка: {value}%";
- });
-
- _cancellationTokenSource = new CancellationTokenSource();
-
- // Определяем тип файла
- if (projectFilePath.EndsWith(".sln", StringComparison.OrdinalIgnoreCase))
- {
- await LoadSolutionAsync(projectFilePath, progress, _cancellationTokenSource.Token);
- }
- else if (projectFilePath.EndsWith(".csproj", StringComparison.OrdinalIgnoreCase))
- {
- await LoadProjectDirectoryAsync(projectDirectory, progress, _cancellationTokenSource.Token);
- }
-
- IsProjectLoaded = true;
- ProgressText = "Проект загружен успешно";
- }
- catch (OperationCanceledException)
- {
- ProgressText = "Загрузка отменена";
- RootDirectory = null;
- IsProjectLoaded = false;
- }
- catch (Exception ex)
- {
- MessageBox.Show($"Ошибка при загрузке проекта: {ex.Message}", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
- RootDirectory = null;
- IsProjectLoaded = false;
- }
- finally
- {
- IsProcessing = false;
- }
- }
-
- private async Task LoadSolutionAsync(string solutionPath, IProgress progress, CancellationToken cancellationToken)
- {
- try
- {
- string solutionDirectory = Path.GetDirectoryName(solutionPath);
- var solutionProjects = ParseSolutionProjects(solutionPath);
-
- var solutionItem = new FileItem
- {
- Name = Path.GetFileName(solutionPath),
- FullName = solutionPath,
- IsDirectory = true,
- IsSelected = false
- };
-
- int totalProjects = solutionProjects.Count;
- int processedProjects = 0;
-
- foreach (var projectPath in solutionProjects)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- if (File.Exists(projectPath))
- {
- string projectDir = Path.GetDirectoryName(projectPath);
- string projectName = Path.GetFileName(projectDir);
-
- var projectItem = new FileItem
- {
- Name = projectName,
- FullName = projectDir,
- IsDirectory = true,
- Parent = solutionItem,
- IsSelected = false
- };
-
- await ProjectScannerService.BuildDirectoryTreeAsync(projectDir, projectItem, progress, cancellationToken);
-
- if (projectItem.Children.Any())
- {
- solutionItem.Children.Add(projectItem);
- }
- }
-
- processedProjects++;
- progress?.Report((int)((processedProjects * 100.0) / totalProjects));
- }
-
- RootDirectory = solutionItem;
-
- // После загрузки решения - не выбираем ничего по умолчанию
- ClearAllSelections(solutionItem);
- }
- catch (Exception ex)
- {
- throw new Exception($"Ошибка при обработке решения: {ex.Message}", ex);
- }
- }
-
- private List ParseSolutionProjects(string solutionPath)
- {
- var projects = new List();
- try
- {
- string solutionDirectory = Path.GetDirectoryName(solutionPath);
-
- foreach (string line in File.ReadAllLines(solutionPath))
- {
- if (line.Trim().StartsWith("Project(", StringComparison.OrdinalIgnoreCase))
- {
- var parts = line.Split(new[] { '"' }, StringSplitOptions.RemoveEmptyEntries);
- if (parts.Length >= 3)
- {
- string relativePath = parts[2].Trim();
- string absolutePath = Path.GetFullPath(Path.Combine(solutionDirectory, relativePath));
-
- if (absolutePath.EndsWith(".csproj", StringComparison.OrdinalIgnoreCase) && File.Exists(absolutePath))
- {
- projects.Add(absolutePath);
- }
- }
- }
- }
- }
- catch (Exception ex)
- {
- throw new Exception($"Ошибка парсинга файла решения: {ex.Message}", ex);
- }
- return projects;
- }
-
- private async Task LoadProjectDirectoryAsync(string projectDirectory, IProgress progress, CancellationToken cancellationToken)
- {
- try
- {
- var projectName = Path.GetFileName(projectDirectory);
- var rootItem = new FileItem
- {
- Name = projectName,
- FullName = projectDirectory,
- IsDirectory = true,
- IsSelected = false
- };
-
- await ProjectScannerService.BuildDirectoryTreeAsync(projectDirectory, rootItem, progress, cancellationToken);
-
- RootDirectory = rootItem;
-
- // После загрузки проекта - не выбираем ничего по умолчанию
- ClearAllSelections(rootItem);
- }
- catch (Exception ex)
- {
- throw new Exception($"Ошибка при загрузке проекта: {ex.Message}", ex);
- }
- }
-
- private void ClearAllSelections(FileItem item)
- {
- item.IsSelected = false;
- foreach (var child in item.Children)
- {
- ClearAllSelections(child);
- }
- }
-
- private async void GenerateFileAsync(object parameter)
- {
- if (!CanGenerate || RootDirectory == null) return;
-
- var selectedFiles = GetSelectedFiles(RootDirectory);
-
- if (selectedFiles.Count == 0)
- {
- MessageBox.Show("Пожалуйста, выберите хотя бы один файл для обработки.", "Предупреждение", MessageBoxButton.OK, MessageBoxImage.Warning);
- return;
- }
-
- var saveDialog = new SaveFileDialog
- {
- Title = "Сохранить контекстный файл",
- Filter = "Текстовые файлы (*.txt)|*.txt|Все файлы (*.*)|*.*",
- FileName = $"{Path.GetFileNameWithoutExtension(SelectedProjectFilePath)}_context.txt",
- DefaultExt = ".txt"
- };
-
- if (!string.IsNullOrEmpty(_lastProjectPath) && Directory.Exists(_lastProjectPath))
- {
- saveDialog.InitialDirectory = _lastProjectPath;
- }
-
- bool? result = saveDialog.ShowDialog();
-
- if (result == true)
- {
- IsProcessing = true;
- ProgressText = "Генерация контекстного файла...";
- ProgressValue = 0;
-
- try
- {
- _cancellationTokenSource = new CancellationTokenSource();
- var progress = new Progress(value =>
- {
- ProgressValue = value;
- ProgressText = $"Генерация: {value}%";
- });
-
- await GenerateContextFileAsync(selectedFiles, saveDialog.FileName, progress, _cancellationTokenSource.Token);
-
- MessageBox.Show($"Файл успешно создан:\n{saveDialog.FileName}", "Успех", MessageBoxButton.OK, MessageBoxImage.Information);
- }
- catch (OperationCanceledException)
- {
- ProgressText = "Генерация отменена";
- }
- catch (IOException ex)
- {
- MessageBox.Show($"Ошибка доступа к файлу: {ex.Message}\nПожалуйста, закройте файлы или предоставьте необходимые права доступа.", "Ошибка доступа", MessageBoxButton.OK, MessageBoxImage.Error);
- }
- catch (Exception ex)
- {
- MessageBox.Show($"Ошибка при генерации файла: {ex.Message}", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
- }
- finally
- {
- IsProcessing = false;
- }
- }
- }
-
- private List GetSelectedFiles(FileItem rootItem)
- {
- var selectedFiles = new List();
- CollectSelectedFiles(rootItem, selectedFiles);
- return selectedFiles;
- }
-
- private void CollectSelectedFiles(FileItem item, List selectedFiles)
- {
- if (item.IsDirectory)
- {
- foreach (var child in item.Children)
- {
- CollectSelectedFiles(child, selectedFiles);
- }
- }
- else if (item.IsSelected == true)
- {
- selectedFiles.Add(item.FullName);
- }
- }
-
- private async Task GenerateContextFileAsync(List selectedFiles, string outputPath, IProgress progress, CancellationToken cancellationToken)
- {
- var outputContent = new StringBuilder();
- int totalFiles = selectedFiles.Count;
- int processedFiles = 0;
-
- foreach (var filePath in selectedFiles)
+ foreach (var projectPath in solutionProjects)
{
cancellationToken.ThrowIfCancellationRequested();
- try
+ if (File.Exists(projectPath))
{
- string projectDir = Path.GetDirectoryName(SelectedProjectFilePath);
- string relativePath = Path.GetRelativePath(projectDir, filePath);
- string fileContent = await File.ReadAllTextAsync(filePath, Encoding.UTF8);
+ string? projectDir = Path.GetDirectoryName(projectPath);
+ string? projectName = Path.GetFileName(projectDir);
- // Обработка комментариев
- fileContent = FileProcessorService.ProcessFileContent(fileContent, Path.GetFileName(filePath));
- fileContent = FileProcessorService.RemoveMultiLineComments(fileContent);
+ var projectItem = new FileItem
+ {
+ Name = projectName,
+ FullName = projectDir,
+ IsDirectory = true,
+ Parent = solutionItem,
+ IsSelected = false
+ };
- outputContent.AppendLine($"=== Файл: {relativePath} ===");
- outputContent.AppendLine(fileContent);
- outputContent.AppendLine(); // Пустая строка между файлами
+ await ProjectScannerService.BuildDirectoryTreeAsync(projectDir, projectItem, progress, cancellationToken);
- processedFiles++;
- int progressValue = (int)((processedFiles * 100.0) / totalFiles);
- progress?.Report(progressValue);
- }
- catch (IOException ex)
- {
- throw new IOException($"Файл '{Path.GetFileName(filePath)}' заблокирован или недоступен: {ex.Message}", ex);
+ if (projectItem.Children.Any())
+ {
+ solutionItem.Children.Add(projectItem);
+ }
}
+
+ processedProjects++;
+ progress?.Report((int)((processedProjects * 100.0) / totalProjects));
}
- // Сохраняем результат с кодировкой UTF-8 с BOM
- var encoding = new UTF8Encoding(true);
- await File.WriteAllTextAsync(outputPath, outputContent.ToString(), encoding);
+ RootDirectory = solutionItem;
+
+ // После загрузки решения - не выбираем ничего по умолчанию
+ ClearAllSelections(solutionItem);
}
-
- private void CancelProcessing(object parameter)
+ catch (Exception ex)
{
- _cancellationTokenSource?.Cancel();
- }
-
- private void LoadSettings()
- {
- try
- {
- _lastProjectPath = Properties.Settings.Default.LastProjectPath;
- }
- catch
- {
- _lastProjectPath = null;
- }
- }
-
- private void SaveSettings()
- {
- try
- {
- Properties.Settings.Default.LastProjectPath = _lastProjectPath;
- Properties.Settings.Default.Save();
- }
- catch
- {
- // Игнорируем ошибки сохранения настроек
- }
- }
-
- public event PropertyChangedEventHandler? PropertyChanged;
-
- protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
- {
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ throw new Exception($"Ошибка при обработке решения: {ex.Message}", ex);
}
}
- public class RelayCommand : ICommand
+ private static List ParseSolutionProjects(string solutionPath)
{
- private readonly Action