diff --git a/src/SharpIDE.Godot/Features/Problems/DiagnosticMetadataContainer.cs b/src/SharpIDE.Godot/Features/Problems/DiagnosticMetadataContainer.cs new file mode 100644 index 0000000..a921e21 --- /dev/null +++ b/src/SharpIDE.Godot/Features/Problems/DiagnosticMetadataContainer.cs @@ -0,0 +1,15 @@ +using Godot; +using Microsoft.CodeAnalysis; +using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; + +namespace SharpIDE.Godot.Features.Problems; + +public partial class DiagnosticMetadataContainer(Diagnostic diagnostic) : GodotObject +{ + public Diagnostic Diagnostic { get; } = diagnostic; +} + +public partial class ProjectContainer(SharpIdeProjectModel project) : GodotObject +{ + public SharpIdeProjectModel Project { get; } = project; +} \ No newline at end of file diff --git a/src/SharpIDE.Godot/Features/Problems/DiagnosticMetadataContainer.cs.uid b/src/SharpIDE.Godot/Features/Problems/DiagnosticMetadataContainer.cs.uid new file mode 100644 index 0000000..671c792 --- /dev/null +++ b/src/SharpIDE.Godot/Features/Problems/DiagnosticMetadataContainer.cs.uid @@ -0,0 +1 @@ +uid://bpo4ti23q3bhu diff --git a/src/SharpIDE.Godot/Features/Problems/ProblemsPanel.cs b/src/SharpIDE.Godot/Features/Problems/ProblemsPanel.cs index 7281d79..20270a0 100644 --- a/src/SharpIDE.Godot/Features/Problems/ProblemsPanel.cs +++ b/src/SharpIDE.Godot/Features/Problems/ProblemsPanel.cs @@ -3,6 +3,7 @@ using Godot; using Microsoft.CodeAnalysis; using ObservableCollections; using R3; +using SharpIDE.Application.Features.SolutionDiscovery; using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; namespace SharpIDE.Godot.Features.Problems; @@ -26,6 +27,7 @@ public partial class ProblemsPanel : Control public override void _Ready() { _tree = GetNode("%Tree"); + _tree.ItemActivated += TreeOnItemActivated; _rootItem = _tree.CreateItem(); _rootItem.SetText(0, "Problems"); Observable.EveryValueChanged(this, manager => manager.Solution) @@ -62,6 +64,7 @@ public partial class ProblemsPanel : Control var treeItem = tree.CreateItem(parent); treeItem.SetText(0, e.NewItem.Value.Name); treeItem.SetIcon(0, CsprojIcon); + treeItem.SetMetadata(0, new ProjectContainer(e.NewItem.Value)); e.NewItem.View.Value = treeItem; Observable.EveryValueChanged(e.NewItem.Value, s => s.Diagnostics.Count).Subscribe(s => treeItem.Visible = s is not 0).AddTo(this); @@ -83,6 +86,7 @@ public partial class ProblemsPanel : Control { var diagItem = tree.CreateItem(parent); diagItem.SetText(0, e.NewItem.Value.GetMessage()); + diagItem.SetMetadata(0, new DiagnosticMetadataContainer(e.NewItem.Value)); diagItem.SetIcon(0, e.NewItem.Value.Severity switch { DiagnosticSeverity.Error => ErrorIcon, @@ -100,4 +104,26 @@ public partial class ProblemsPanel : Control treeItem?.Free(); }); } + + private void TreeOnItemActivated() + { + var selected = _tree.GetSelected(); + var diagnosticContainer = selected.GetMetadata(0).As(); + if (diagnosticContainer is null) return; + var diagnostic = diagnosticContainer.Diagnostic; + var parentTreeItem = selected.GetParent(); + var projectContainer = parentTreeItem.GetMetadata(0).As(); + if (projectContainer is null) return; + var projectModel = projectContainer.Project; + OpenDocumentContainingDiagnostic(diagnostic, projectModel); + } + + private static void OpenDocumentContainingDiagnostic(Diagnostic diagnostic, SharpIdeProjectModel projectModel) + { + // TODO: probably store a flat list of all files in each project to avoid recursion + var file = projectModel.Files + .Concat(projectModel.Folders.SelectMany(f => f.GetAllFiles())) + .Single(s => s.Path == diagnostic.Location.SourceTree?.GetMappedLineSpan(diagnostic.Location.SourceSpan).Path); + GodotGlobalEvents.InvokeFileSelected(file); + } } \ No newline at end of file diff --git a/src/SharpIDE.Godot/Features/SolutionExplorer/SolutionExplorerPanel.cs b/src/SharpIDE.Godot/Features/SolutionExplorer/SolutionExplorerPanel.cs index f66ff7b..7e4ca55 100644 --- a/src/SharpIDE.Godot/Features/SolutionExplorer/SolutionExplorerPanel.cs +++ b/src/SharpIDE.Godot/Features/SolutionExplorer/SolutionExplorerPanel.cs @@ -7,9 +7,6 @@ namespace SharpIDE.Godot.Features.SolutionExplorer; public partial class SolutionExplorerPanel : MarginContainer { - [Signal] - public delegate void FileSelectedEventHandler(SharpIdeFileGodotContainer file); - [Export] public Texture2D CsharpFileIcon { get; set; } = null!; [Export] @@ -37,7 +34,7 @@ public partial class SolutionExplorerPanel : MarginContainer if (sharpIdeFileContainer is null) return; var sharpIdeFile = sharpIdeFileContainer.File; Guard.Against.Null(sharpIdeFile, nameof(sharpIdeFile)); - EmitSignalFileSelected(sharpIdeFileContainer); + GodotGlobalEvents.InvokeFileSelected(sharpIdeFile); } public void RepopulateTree() diff --git a/src/SharpIDE.Godot/GodotGlobalEvents.cs b/src/SharpIDE.Godot/GodotGlobalEvents.cs index fe968df..fbdabc4 100644 --- a/src/SharpIDE.Godot/GodotGlobalEvents.cs +++ b/src/SharpIDE.Godot/GodotGlobalEvents.cs @@ -1,4 +1,5 @@ using SharpIDE.Application.Features.Events; +using SharpIDE.Application.Features.SolutionDiscovery; namespace SharpIDE.Godot; @@ -12,6 +13,9 @@ public static class GodotGlobalEvents public static event Func BottomPanelVisibilityChangeRequested = _ => Task.CompletedTask; public static void InvokeBottomPanelVisibilityChangeRequested(bool show) => BottomPanelVisibilityChangeRequested.InvokeParallelFireAndForget(show); + + public static event Func FileSelected = _ => Task.CompletedTask; + public static void InvokeFileSelected(SharpIdeFile file) => FileSelected.InvokeParallelFireAndForget(file); } public enum BottomPanelType diff --git a/src/SharpIDE.Godot/IdeRoot.cs b/src/SharpIDE.Godot/IdeRoot.cs index d5700ed..febdda1 100644 --- a/src/SharpIDE.Godot/IdeRoot.cs +++ b/src/SharpIDE.Godot/IdeRoot.cs @@ -1,6 +1,7 @@ using Godot; using Microsoft.Build.Locator; using SharpIDE.Application.Features.Analysis; +using SharpIDE.Application.Features.SolutionDiscovery; using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; using SharpIDE.Godot.Features.BottomPanel; using SharpIDE.Godot.Features.CustomControls; @@ -39,7 +40,7 @@ public partial class IdeRoot : Control _bottomPanelManager = GetNode("%BottomPanel"); _runMenuButton.Pressed += OnRunMenuButtonPressed; - _solutionExplorerPanel.FileSelected += OnSolutionExplorerPanelOnFileSelected; + GodotGlobalEvents.FileSelected += OnSolutionExplorerPanelOnFileSelected; _fileDialog.FileSelected += OnFileSelected; _openSlnButton.Pressed += () => _fileDialog.Visible = true; _buildSlnButton.Pressed += OnBuildSlnButtonPressed; @@ -61,9 +62,9 @@ public partial class IdeRoot : Control await Singletons.BuildService.MsBuildSolutionAsync(_solutionExplorerPanel.SolutionModel.FilePath); } - private async void OnSolutionExplorerPanelOnFileSelected(SharpIdeFileGodotContainer file) + private async Task OnSolutionExplorerPanelOnFileSelected(SharpIdeFile file) { - await _sharpIdeCodeEdit.SetSharpIdeFile(file.File); + await _sharpIdeCodeEdit.SetSharpIdeFile(file); } private void OnFileSelected(string path)