diff --git a/src/SharpIDE.Application/Features/SolutionDiscovery/SharpIdeFile.cs b/src/SharpIDE.Application/Features/SolutionDiscovery/SharpIdeFile.cs index 0eb3df5..b4572ea 100644 --- a/src/SharpIDE.Application/Features/SolutionDiscovery/SharpIdeFile.cs +++ b/src/SharpIDE.Application/Features/SolutionDiscovery/SharpIdeFile.cs @@ -10,10 +10,11 @@ public class SharpIdeFile : ISharpIdeNode, IChildSharpIdeNode public required string Name { get; set; } [SetsRequiredMembers] - internal SharpIdeFile(string fullPath, string name, IExpandableSharpIdeNode parent) + internal SharpIdeFile(string fullPath, string name, IExpandableSharpIdeNode parent, HashSet allFiles) { Path = fullPath; Name = name; Parent = parent; + allFiles.Add(this); } } diff --git a/src/SharpIDE.Application/Features/SolutionDiscovery/SharpIdeFolder.cs b/src/SharpIDE.Application/Features/SolutionDiscovery/SharpIdeFolder.cs index cb2c157..5df4c59 100644 --- a/src/SharpIDE.Application/Features/SolutionDiscovery/SharpIdeFolder.cs +++ b/src/SharpIDE.Application/Features/SolutionDiscovery/SharpIdeFolder.cs @@ -13,13 +13,13 @@ public class SharpIdeFolder : ISharpIdeNode, IExpandableSharpIdeNode, IChildShar public bool Expanded { get; set; } [SetsRequiredMembers] - public SharpIdeFolder(DirectoryInfo folderInfo, IExpandableSharpIdeNode parent) + public SharpIdeFolder(DirectoryInfo folderInfo, IExpandableSharpIdeNode parent, HashSet allFiles) { Parent = parent; Path = folderInfo.FullName; Name = folderInfo.Name; - Files = folderInfo.GetFiles(this); - Folders = this.GetSubFolders(this); + Files = folderInfo.GetFiles(this, allFiles); + Folders = this.GetSubFolders(this, allFiles); } public SharpIdeFolder() diff --git a/src/SharpIDE.Application/Features/SolutionDiscovery/TreeMapperV2.cs b/src/SharpIDE.Application/Features/SolutionDiscovery/TreeMapperV2.cs index 4c42f29..f50f945 100644 --- a/src/SharpIDE.Application/Features/SolutionDiscovery/TreeMapperV2.cs +++ b/src/SharpIDE.Application/Features/SolutionDiscovery/TreeMapperV2.cs @@ -10,7 +10,7 @@ public static class TreeMapperV2 return folder.Files .Concat(folder.Folders.SelectMany(sub => sub.GetAllFiles())); } - public static List GetSubFolders(string csprojectPath, SharpIdeProjectModel sharpIdeProjectModel) + public static List GetSubFolders(string csprojectPath, SharpIdeProjectModel sharpIdeProjectModel, HashSet allFiles) { var projectDirectory = Path.GetDirectoryName(csprojectPath)!; var rootFolder = new SharpIdeFolder @@ -21,12 +21,12 @@ public static class TreeMapperV2 Files = [], Folders = [] }; - var subFolders = rootFolder.GetSubFolders(sharpIdeProjectModel); + var subFolders = rootFolder.GetSubFolders(sharpIdeProjectModel, allFiles); return subFolders; } private static readonly string[] _excludedFolders = ["bin", "obj", "node_modules"]; - public static List GetSubFolders(this SharpIdeFolder folder, IExpandableSharpIdeNode parent) + public static List GetSubFolders(this SharpIdeFolder folder, IExpandableSharpIdeNode parent, HashSet allFiles) { var directoryInfo = new DirectoryInfo(folder.Path); ConcurrentBag subFolders = []; @@ -47,20 +47,20 @@ public static class TreeMapperV2 Parallel.ForEach(subFolderInfos, subFolderInfo => { - var subFolder = new SharpIdeFolder(subFolderInfo, parent); + var subFolder = new SharpIdeFolder(subFolderInfo, parent, allFiles); subFolders.Add(subFolder); }); return subFolders.ToList(); } - public static List GetFiles(string csprojectPath, SharpIdeProjectModel sharpIdeProjectModel) + public static List GetFiles(string csprojectPath, SharpIdeProjectModel sharpIdeProjectModel, HashSet allFiles) { var projectDirectory = Path.GetDirectoryName(csprojectPath)!; var directoryInfo = new DirectoryInfo(projectDirectory); - return GetFiles(directoryInfo, sharpIdeProjectModel); + return GetFiles(directoryInfo, sharpIdeProjectModel, allFiles); } - public static List GetFiles(this DirectoryInfo directoryInfo, IExpandableSharpIdeNode parent) + public static List GetFiles(this DirectoryInfo directoryInfo, IExpandableSharpIdeNode parent, HashSet allFiles) { List fileInfos; try @@ -72,7 +72,7 @@ public static class TreeMapperV2 return []; } - return fileInfos.Select(f => new SharpIdeFile(f.FullName, f.Name, parent) + return fileInfos.Select(f => new SharpIdeFile(f.FullName, f.Name, parent, allFiles) { Path = f.FullName, Name = f.Name, diff --git a/src/SharpIDE.Application/Features/SolutionDiscovery/VsPersistence/SharpIdeModels.cs b/src/SharpIDE.Application/Features/SolutionDiscovery/VsPersistence/SharpIdeModels.cs index 41cea11..8b59596 100644 --- a/src/SharpIDE.Application/Features/SolutionDiscovery/VsPersistence/SharpIdeModels.cs +++ b/src/SharpIDE.Application/Features/SolutionDiscovery/VsPersistence/SharpIdeModels.cs @@ -34,6 +34,7 @@ public class SharpIdeSolutionModel : ISharpIdeNode, IExpandableSharpIdeNode public required List Projects { get; set; } public required List Folders { get; set; } public required HashSet AllProjects { get; set; } + public required HashSet AllFiles { get; set; } public bool Expanded { get; set; } [SetsRequiredMembers] @@ -41,10 +42,11 @@ public class SharpIdeSolutionModel : ISharpIdeNode, IExpandableSharpIdeNode { var solutionName = Path.GetFileName(solutionFilePath); AllProjects = []; + AllFiles = []; Name = solutionName; FilePath = solutionFilePath; - Projects = intermediateModel.Projects.Select(s => new SharpIdeProjectModel(s, AllProjects, this)).ToList(); - Folders = intermediateModel.SolutionFolders.Select(s => new SharpIdeSolutionFolder(s, AllProjects, this)).ToList(); + Projects = intermediateModel.Projects.Select(s => new SharpIdeProjectModel(s, AllProjects, AllFiles, this)).ToList(); + Folders = intermediateModel.SolutionFolders.Select(s => new SharpIdeSolutionFolder(s, AllProjects, AllFiles, this)).ToList(); } } public class SharpIdeSolutionFolder : ISharpIdeNode, IExpandableSharpIdeNode, IChildSharpIdeNode @@ -57,13 +59,13 @@ public class SharpIdeSolutionFolder : ISharpIdeNode, IExpandableSharpIdeNode, IC public required IExpandableSharpIdeNode Parent { get; set; } [SetsRequiredMembers] - internal SharpIdeSolutionFolder(IntermediateSlnFolderModel intermediateModel, HashSet allProjects, IExpandableSharpIdeNode parent) + internal SharpIdeSolutionFolder(IntermediateSlnFolderModel intermediateModel, HashSet allProjects, HashSet allFiles, IExpandableSharpIdeNode parent) { Name = intermediateModel.Model.Name; Parent = parent; - Files = intermediateModel.Files.Select(s => new SharpIdeFile(s.FullPath, s.Name, this)).ToList(); - Folders = intermediateModel.Folders.Select(x => new SharpIdeSolutionFolder(x, allProjects, this)).ToList(); - Projects = intermediateModel.Projects.Select(x => new SharpIdeProjectModel(x, allProjects, this)).ToList(); + Files = intermediateModel.Files.Select(s => new SharpIdeFile(s.FullPath, s.Name, this, allFiles)).ToList(); + Folders = intermediateModel.Folders.Select(x => new SharpIdeSolutionFolder(x, allProjects, allFiles, this)).ToList(); + Projects = intermediateModel.Projects.Select(x => new SharpIdeProjectModel(x, allProjects, allFiles, this)).ToList(); } } public class SharpIdeProjectModel : ISharpIdeNode, IExpandableSharpIdeNode, IChildSharpIdeNode @@ -79,13 +81,13 @@ public class SharpIdeProjectModel : ISharpIdeNode, IExpandableSharpIdeNode, IChi public required Task MsBuildEvaluationProjectTask { get; set; } [SetsRequiredMembers] - internal SharpIdeProjectModel(IntermediateProjectModel projectModel, HashSet allProjects, IExpandableSharpIdeNode parent) + internal SharpIdeProjectModel(IntermediateProjectModel projectModel, HashSet allProjects, HashSet allFiles, IExpandableSharpIdeNode parent) { Parent = parent; Name = projectModel.Model.ActualDisplayName; FilePath = projectModel.FullFilePath; - Files = TreeMapperV2.GetFiles(projectModel.FullFilePath, this); - Folders = TreeMapperV2.GetSubFolders(projectModel.FullFilePath, this); + Files = TreeMapperV2.GetFiles(projectModel.FullFilePath, this, allFiles); + Folders = TreeMapperV2.GetSubFolders(projectModel.FullFilePath, this, allFiles); MsBuildEvaluationProjectTask = ProjectEvaluation.GetProject(projectModel.FullFilePath); allProjects.Add(this); } diff --git a/src/SharpIDE.Godot/Features/SolutionExplorer/SolutionExplorerPanel.cs b/src/SharpIDE.Godot/Features/SolutionExplorer/SolutionExplorerPanel.cs index 1143017..cf76f30 100644 --- a/src/SharpIDE.Godot/Features/SolutionExplorer/SolutionExplorerPanel.cs +++ b/src/SharpIDE.Godot/Features/SolutionExplorer/SolutionExplorerPanel.cs @@ -107,6 +107,7 @@ public partial class SolutionExplorerPanel : Panel var fileItem = _tree.CreateItem(parent); fileItem.SetText(0, file.Name); var container = new SharpIdeFileGodotContainer { File = file }; + // TODO: Handle ObjectDB instances leaked at exit fileItem.SetMetadata(0, container); } diff --git a/src/SharpIDE.Godot/IdeRoot.cs b/src/SharpIDE.Godot/IdeRoot.cs index 0c1f7dc..a109881 100644 --- a/src/SharpIDE.Godot/IdeRoot.cs +++ b/src/SharpIDE.Godot/IdeRoot.cs @@ -58,6 +58,7 @@ public partial class IdeRoot : Control GD.Print($"Selected: {path}"); var solutionModel = await VsPersistenceMapper.GetSolutionModel(path); _solutionExplorerPanel.SolutionModel = solutionModel; + _sharpIdeCodeEdit.Solution = solutionModel; Callable.From(_solutionExplorerPanel.RepopulateTree).CallDeferred(); RoslynAnalysis.StartSolutionAnalysis(path); diff --git a/src/SharpIDE.Godot/SharpIdeCodeEdit.cs b/src/SharpIDE.Godot/SharpIdeCodeEdit.cs index 5574862..130a93b 100644 --- a/src/SharpIDE.Godot/SharpIdeCodeEdit.cs +++ b/src/SharpIDE.Godot/SharpIdeCodeEdit.cs @@ -14,6 +14,7 @@ using SharpIDE.Application.Features.Analysis; using SharpIDE.Application.Features.Debugging; using SharpIDE.Application.Features.Events; using SharpIDE.Application.Features.SolutionDiscovery; +using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; using SharpIDE.Godot.Features.Run; using Task = System.Threading.Tasks.Task; @@ -28,6 +29,7 @@ public partial class SharpIdeCodeEdit : CodeEdit private int _selectionStartCol; private int _selectionEndCol; + public SharpIdeSolutionModel? Solution { get; set; } private SharpIdeFile _currentFile = null!; private CustomHighlighter _syntaxHighlighter = new(); @@ -36,6 +38,7 @@ public partial class SharpIdeCodeEdit : CodeEdit private ImmutableArray<(FileLinePositionSpan fileSpan, Diagnostic diagnostic)> _diagnostics = []; private ImmutableArray _currentCodeActionsInPopup = []; private ExecutionStopInfo? _executionStopInfo; + private bool _fileChangingSuppressBreakpointToggleEvent; public override void _Ready() { @@ -55,7 +58,12 @@ public partial class SharpIdeCodeEdit : CodeEdit private async Task OnDebuggerExecutionStopped(ExecutionStopInfo executionStopInfo) { - if (executionStopInfo.FilePath != _currentFile.Path) return; // TODO: handle file switching + Guard.Against.Null(Solution, nameof(Solution)); + if (executionStopInfo.FilePath != _currentFile.Path) + { + var file = Solution.AllFiles.Single(s => s.Path == executionStopInfo.FilePath); + await this.InvokeAsync(async () => await SetSharpIdeFile(file)); + } var lineInt = executionStopInfo.Line - 1; // Debugging is 1-indexed, Godot is 0-indexed Guard.Against.Negative(lineInt, nameof(lineInt)); _executionStopInfo = executionStopInfo; @@ -69,6 +77,7 @@ public partial class SharpIdeCodeEdit : CodeEdit private void OnBreakpointToggled(long line) { + if (_fileChangingSuppressBreakpointToggleEvent) return; var lineInt = (int)line; var breakpointAdded = IsLineBreakpointed(lineInt); var lineForDebugger = lineInt + 1; // Godot is 0-indexed, Debugging is 1-indexed @@ -157,7 +166,9 @@ public partial class SharpIdeCodeEdit : CodeEdit { _currentFile = file; var fileContents = await File.ReadAllTextAsync(_currentFile.Path); + _fileChangingSuppressBreakpointToggleEvent = true; SetText(fileContents); + _fileChangingSuppressBreakpointToggleEvent = false; var syntaxHighlighting = await RoslynAnalysis.GetDocumentSyntaxHighlighting(_currentFile); SetSyntaxHighlightingModel(syntaxHighlighting); var diagnostics = await RoslynAnalysis.GetDocumentDiagnostics(_currentFile);