Initial commit
This commit is contained in:
@@ -0,0 +1,276 @@
|
||||
using RobotNet10.ScriptEngine.Shared;
|
||||
using static System.Runtime.InteropServices.JavaScript.JSType;
|
||||
|
||||
namespace RobotNet10.ScriptEditor.Models;
|
||||
|
||||
public class ScriptFolder(ScriptFolderDto data, ScriptFolder? parent = null) : IHierarchyItem
|
||||
{
|
||||
public ScriptFolder? Parent => parent;
|
||||
public string Path => System.IO.Path.Combine(parent?.Path ?? "", Name);
|
||||
public int Level { get; } = data.Level;
|
||||
|
||||
public bool IsModified { get; private set; }
|
||||
public int WarningCount { get; private set; }
|
||||
public int ErrorCount { get; private set; }
|
||||
public bool IsExpanded { get; set; } = false;
|
||||
|
||||
public event Action? ChildrenChanged;
|
||||
public event Action? Modified;
|
||||
public event Action? NameChanged;
|
||||
public event Action<int, int>? DiagnosticsChanged;
|
||||
|
||||
public IEnumerable<ScriptFolder> Folders => WorkspaceFolders;
|
||||
public IEnumerable<ScriptFile> Files => WorkspaceFiles;
|
||||
|
||||
private string _name = data.Name;
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value)) throw new ArgumentNullException($"Tên file {_name} không được để trống");
|
||||
|
||||
if (_name == value) return;
|
||||
|
||||
_name = value;
|
||||
|
||||
NameChanged?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<ScriptFolder> WorkspaceFolders = [];
|
||||
private readonly List<ScriptFile> WorkspaceFiles = [];
|
||||
|
||||
public void AddFiles(params IEnumerable<ScriptFile> files)
|
||||
{
|
||||
WorkspaceFiles.AddRange(files);
|
||||
WorkspaceFiles.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.Ordinal));
|
||||
|
||||
// Recalculate totals including new files
|
||||
var totalWarningCount = Files.Sum(file => file.WarningCount) + Folders.Sum(folder => folder.WarningCount);
|
||||
var totalErrorCount = Files.Sum(file => file.ErrorCount) + Folders.Sum(folder => folder.ErrorCount);
|
||||
var newIsModified = Files.Any(file => file.IsModified) || Folders.Any(folder => folder.IsModified);
|
||||
|
||||
var warningChanged = WarningCount != totalWarningCount;
|
||||
var errorChanged = ErrorCount != totalErrorCount;
|
||||
var modifiedChanged = IsModified != newIsModified;
|
||||
|
||||
WarningCount = totalWarningCount;
|
||||
ErrorCount = totalErrorCount;
|
||||
IsModified = newIsModified;
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
file.Modified += UpdateModified;
|
||||
file.DiagnosticsChanged += OnDiagnosticsChanged;
|
||||
}
|
||||
|
||||
// Notify changes
|
||||
if (warningChanged || errorChanged)
|
||||
{
|
||||
DiagnosticsChanged?.Invoke(WarningCount, ErrorCount);
|
||||
}
|
||||
if (modifiedChanged)
|
||||
{
|
||||
Modified?.Invoke();
|
||||
}
|
||||
|
||||
ChildrenChanged?.Invoke();
|
||||
}
|
||||
|
||||
public void AddFolders(params IEnumerable<ScriptFolder> folders)
|
||||
{
|
||||
WorkspaceFolders.AddRange(folders);
|
||||
WorkspaceFolders.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.Ordinal));
|
||||
|
||||
// Recalculate totals including new folders
|
||||
var totalWarningCount = Files.Sum(file => file.WarningCount) + Folders.Sum(folder => folder.WarningCount);
|
||||
var totalErrorCount = Files.Sum(file => file.ErrorCount) + Folders.Sum(folder => folder.ErrorCount);
|
||||
var newIsModified = Files.Any(file => file.IsModified) || Folders.Any(folder => folder.IsModified);
|
||||
|
||||
var warningChanged = WarningCount != totalWarningCount;
|
||||
var errorChanged = ErrorCount != totalErrorCount;
|
||||
var modifiedChanged = IsModified != newIsModified;
|
||||
|
||||
WarningCount = totalWarningCount;
|
||||
ErrorCount = totalErrorCount;
|
||||
IsModified = newIsModified;
|
||||
|
||||
foreach (var folder in folders)
|
||||
{
|
||||
folder.Modified += UpdateModified;
|
||||
folder.DiagnosticsChanged += OnDiagnosticsChanged;
|
||||
}
|
||||
|
||||
// Notify changes
|
||||
if (warningChanged || errorChanged)
|
||||
{
|
||||
DiagnosticsChanged?.Invoke(WarningCount, ErrorCount);
|
||||
}
|
||||
if (modifiedChanged)
|
||||
{
|
||||
Modified?.Invoke();
|
||||
}
|
||||
|
||||
ChildrenChanged?.Invoke();
|
||||
}
|
||||
|
||||
public void RemoveFile(ScriptFile file)
|
||||
{
|
||||
if (WorkspaceFiles.Remove(file))
|
||||
{
|
||||
// Unsubscribe from file events
|
||||
file.Modified -= UpdateModified;
|
||||
file.DiagnosticsChanged -= OnDiagnosticsChanged;
|
||||
|
||||
// Recalculate totals after removal
|
||||
var totalWarningCount = Files.Sum(f => f.WarningCount) + Folders.Sum(folder => folder.WarningCount);
|
||||
var totalErrorCount = Files.Sum(f => f.ErrorCount) + Folders.Sum(folder => folder.ErrorCount);
|
||||
var newIsModified = Files.Any(f => f.IsModified) || Folders.Any(folder => folder.IsModified);
|
||||
|
||||
var warningChanged = WarningCount != totalWarningCount;
|
||||
var errorChanged = ErrorCount != totalErrorCount;
|
||||
var modifiedChanged = IsModified != newIsModified;
|
||||
|
||||
WarningCount = totalWarningCount;
|
||||
ErrorCount = totalErrorCount;
|
||||
IsModified = newIsModified;
|
||||
|
||||
// Dispose the removed file
|
||||
file.Dispose();
|
||||
|
||||
// Notify changes
|
||||
if (warningChanged || errorChanged)
|
||||
{
|
||||
DiagnosticsChanged?.Invoke(WarningCount, ErrorCount);
|
||||
}
|
||||
if (modifiedChanged)
|
||||
{
|
||||
Modified?.Invoke();
|
||||
}
|
||||
|
||||
ChildrenChanged?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveFolder(ScriptFolder folder)
|
||||
{
|
||||
if (WorkspaceFolders.Remove(folder))
|
||||
{
|
||||
// Unsubscribe from folder events
|
||||
folder.Modified -= UpdateModified;
|
||||
folder.DiagnosticsChanged -= OnDiagnosticsChanged;
|
||||
|
||||
// Recalculate totals after removal
|
||||
var totalWarningCount = Files.Sum(file => file.WarningCount) + Folders.Sum(f => f.WarningCount);
|
||||
var totalErrorCount = Files.Sum(file => file.ErrorCount) + Folders.Sum(f => f.ErrorCount);
|
||||
var newIsModified = Files.Any(file => file.IsModified) || Folders.Any(f => f.IsModified);
|
||||
|
||||
var warningChanged = WarningCount != totalWarningCount;
|
||||
var errorChanged = ErrorCount != totalErrorCount;
|
||||
var modifiedChanged = IsModified != newIsModified;
|
||||
|
||||
WarningCount = totalWarningCount;
|
||||
ErrorCount = totalErrorCount;
|
||||
IsModified = newIsModified;
|
||||
|
||||
// Dispose the removed folder (will handle its children)
|
||||
folder.Dispose();
|
||||
|
||||
// Notify changes
|
||||
if (warningChanged || errorChanged)
|
||||
{
|
||||
DiagnosticsChanged?.Invoke(WarningCount, ErrorCount);
|
||||
}
|
||||
if (modifiedChanged)
|
||||
{
|
||||
Modified?.Invoke();
|
||||
}
|
||||
|
||||
ChildrenChanged?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateModified()
|
||||
{
|
||||
var newIsModified = Files.Any(file => file.IsModified) || Folders.Any(folder => folder.IsModified);
|
||||
|
||||
// Only update and notify if the value actually changed
|
||||
if (IsModified != newIsModified)
|
||||
{
|
||||
IsModified = newIsModified;
|
||||
Modified?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDiagnosticsChanged(int warningCount, int errorCount)
|
||||
{
|
||||
// Recalculate totals from all children
|
||||
// Note: The parameters (warningCount, errorCount) are the absolute values from the child that changed,
|
||||
// but we recalculate from all children to ensure accuracy, especially when multiple files change simultaneously
|
||||
var totalWarningCount = Files.Sum(file => file.WarningCount) + Folders.Sum(folder => folder.WarningCount);
|
||||
var totalErrorCount = Files.Sum(file => file.ErrorCount) + Folders.Sum(folder => folder.ErrorCount);
|
||||
|
||||
if (WarningCount != totalWarningCount || ErrorCount != totalErrorCount)
|
||||
{
|
||||
WarningCount = totalWarningCount;
|
||||
ErrorCount = totalErrorCount;
|
||||
DiagnosticsChanged?.Invoke(WarningCount, ErrorCount);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recalculates WarningCount, ErrorCount, and IsModified from all children.
|
||||
/// This is useful after diagnostics have been updated for all children.
|
||||
/// </summary>
|
||||
internal void RecalculateTotals()
|
||||
{
|
||||
// First, recalculate all child folders recursively
|
||||
foreach (var subFolder in Folders)
|
||||
{
|
||||
subFolder.RecalculateTotals();
|
||||
}
|
||||
|
||||
// Then calculate totals from all children
|
||||
var totalWarningCount = Files.Sum(f => f.WarningCount) + Folders.Sum(f => f.WarningCount);
|
||||
var totalErrorCount = Files.Sum(f => f.ErrorCount) + Folders.Sum(f => f.ErrorCount);
|
||||
var isModified = Files.Any(f => f.IsModified) || Folders.Any(f => f.IsModified);
|
||||
|
||||
// Update properties if changed
|
||||
var warningChanged = WarningCount != totalWarningCount;
|
||||
var errorChanged = ErrorCount != totalErrorCount;
|
||||
var modifiedChanged = IsModified != isModified;
|
||||
|
||||
if (warningChanged || errorChanged || modifiedChanged)
|
||||
{
|
||||
WarningCount = totalWarningCount;
|
||||
ErrorCount = totalErrorCount;
|
||||
IsModified = isModified;
|
||||
|
||||
if (warningChanged || errorChanged)
|
||||
{
|
||||
DiagnosticsChanged?.Invoke(WarningCount, ErrorCount);
|
||||
}
|
||||
|
||||
if (modifiedChanged)
|
||||
{
|
||||
Modified?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var file in WorkspaceFiles)
|
||||
{
|
||||
file.Dispose();
|
||||
}
|
||||
WorkspaceFiles.Clear();
|
||||
foreach (var folder in Folders)
|
||||
{
|
||||
folder.Dispose();
|
||||
}
|
||||
WorkspaceFolders.Clear();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user