open file from problem

This commit is contained in:
Matt Parker
2025-09-13 15:35:52 +10:00
parent 3fb602d8fc
commit b4291cb7f7
6 changed files with 51 additions and 7 deletions

View File

@@ -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;
}

View File

@@ -0,0 +1 @@
uid://bpo4ti23q3bhu

View File

@@ -3,6 +3,7 @@ using Godot;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using ObservableCollections; using ObservableCollections;
using R3; using R3;
using SharpIDE.Application.Features.SolutionDiscovery;
using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence;
namespace SharpIDE.Godot.Features.Problems; namespace SharpIDE.Godot.Features.Problems;
@@ -26,6 +27,7 @@ public partial class ProblemsPanel : Control
public override void _Ready() public override void _Ready()
{ {
_tree = GetNode<Tree>("%Tree"); _tree = GetNode<Tree>("%Tree");
_tree.ItemActivated += TreeOnItemActivated;
_rootItem = _tree.CreateItem(); _rootItem = _tree.CreateItem();
_rootItem.SetText(0, "Problems"); _rootItem.SetText(0, "Problems");
Observable.EveryValueChanged(this, manager => manager.Solution) Observable.EveryValueChanged(this, manager => manager.Solution)
@@ -62,6 +64,7 @@ public partial class ProblemsPanel : Control
var treeItem = tree.CreateItem(parent); var treeItem = tree.CreateItem(parent);
treeItem.SetText(0, e.NewItem.Value.Name); treeItem.SetText(0, e.NewItem.Value.Name);
treeItem.SetIcon(0, CsprojIcon); treeItem.SetIcon(0, CsprojIcon);
treeItem.SetMetadata(0, new ProjectContainer(e.NewItem.Value));
e.NewItem.View.Value = treeItem; e.NewItem.View.Value = treeItem;
Observable.EveryValueChanged(e.NewItem.Value, s => s.Diagnostics.Count).Subscribe(s => treeItem.Visible = s is not 0).AddTo(this); 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); var diagItem = tree.CreateItem(parent);
diagItem.SetText(0, e.NewItem.Value.GetMessage()); diagItem.SetText(0, e.NewItem.Value.GetMessage());
diagItem.SetMetadata(0, new DiagnosticMetadataContainer(e.NewItem.Value));
diagItem.SetIcon(0, e.NewItem.Value.Severity switch diagItem.SetIcon(0, e.NewItem.Value.Severity switch
{ {
DiagnosticSeverity.Error => ErrorIcon, DiagnosticSeverity.Error => ErrorIcon,
@@ -100,4 +104,26 @@ public partial class ProblemsPanel : Control
treeItem?.Free(); treeItem?.Free();
}); });
} }
private void TreeOnItemActivated()
{
var selected = _tree.GetSelected();
var diagnosticContainer = selected.GetMetadata(0).As<DiagnosticMetadataContainer?>();
if (diagnosticContainer is null) return;
var diagnostic = diagnosticContainer.Diagnostic;
var parentTreeItem = selected.GetParent();
var projectContainer = parentTreeItem.GetMetadata(0).As<ProjectContainer?>();
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);
}
} }

View File

@@ -7,9 +7,6 @@ namespace SharpIDE.Godot.Features.SolutionExplorer;
public partial class SolutionExplorerPanel : MarginContainer public partial class SolutionExplorerPanel : MarginContainer
{ {
[Signal]
public delegate void FileSelectedEventHandler(SharpIdeFileGodotContainer file);
[Export] [Export]
public Texture2D CsharpFileIcon { get; set; } = null!; public Texture2D CsharpFileIcon { get; set; } = null!;
[Export] [Export]
@@ -37,7 +34,7 @@ public partial class SolutionExplorerPanel : MarginContainer
if (sharpIdeFileContainer is null) return; if (sharpIdeFileContainer is null) return;
var sharpIdeFile = sharpIdeFileContainer.File; var sharpIdeFile = sharpIdeFileContainer.File;
Guard.Against.Null(sharpIdeFile, nameof(sharpIdeFile)); Guard.Against.Null(sharpIdeFile, nameof(sharpIdeFile));
EmitSignalFileSelected(sharpIdeFileContainer); GodotGlobalEvents.InvokeFileSelected(sharpIdeFile);
} }
public void RepopulateTree() public void RepopulateTree()

View File

@@ -1,4 +1,5 @@
using SharpIDE.Application.Features.Events; using SharpIDE.Application.Features.Events;
using SharpIDE.Application.Features.SolutionDiscovery;
namespace SharpIDE.Godot; namespace SharpIDE.Godot;
@@ -12,6 +13,9 @@ public static class GodotGlobalEvents
public static event Func<bool, Task> BottomPanelVisibilityChangeRequested = _ => Task.CompletedTask; public static event Func<bool, Task> BottomPanelVisibilityChangeRequested = _ => Task.CompletedTask;
public static void InvokeBottomPanelVisibilityChangeRequested(bool show) => BottomPanelVisibilityChangeRequested.InvokeParallelFireAndForget(show); public static void InvokeBottomPanelVisibilityChangeRequested(bool show) => BottomPanelVisibilityChangeRequested.InvokeParallelFireAndForget(show);
public static event Func<SharpIdeFile, Task> FileSelected = _ => Task.CompletedTask;
public static void InvokeFileSelected(SharpIdeFile file) => FileSelected.InvokeParallelFireAndForget(file);
} }
public enum BottomPanelType public enum BottomPanelType

View File

@@ -1,6 +1,7 @@
using Godot; using Godot;
using Microsoft.Build.Locator; using Microsoft.Build.Locator;
using SharpIDE.Application.Features.Analysis; using SharpIDE.Application.Features.Analysis;
using SharpIDE.Application.Features.SolutionDiscovery;
using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence;
using SharpIDE.Godot.Features.BottomPanel; using SharpIDE.Godot.Features.BottomPanel;
using SharpIDE.Godot.Features.CustomControls; using SharpIDE.Godot.Features.CustomControls;
@@ -39,7 +40,7 @@ public partial class IdeRoot : Control
_bottomPanelManager = GetNode<BottomPanelManager>("%BottomPanel"); _bottomPanelManager = GetNode<BottomPanelManager>("%BottomPanel");
_runMenuButton.Pressed += OnRunMenuButtonPressed; _runMenuButton.Pressed += OnRunMenuButtonPressed;
_solutionExplorerPanel.FileSelected += OnSolutionExplorerPanelOnFileSelected; GodotGlobalEvents.FileSelected += OnSolutionExplorerPanelOnFileSelected;
_fileDialog.FileSelected += OnFileSelected; _fileDialog.FileSelected += OnFileSelected;
_openSlnButton.Pressed += () => _fileDialog.Visible = true; _openSlnButton.Pressed += () => _fileDialog.Visible = true;
_buildSlnButton.Pressed += OnBuildSlnButtonPressed; _buildSlnButton.Pressed += OnBuildSlnButtonPressed;
@@ -61,9 +62,9 @@ public partial class IdeRoot : Control
await Singletons.BuildService.MsBuildSolutionAsync(_solutionExplorerPanel.SolutionModel.FilePath); 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) private void OnFileSelected(string path)