use DI in Godot Nodes

This commit is contained in:
Matt Parker
2025-10-18 14:20:04 +10:00
parent 4d67503e13
commit d285ce1bf2
13 changed files with 136 additions and 44 deletions

View File

@@ -9,10 +9,12 @@ namespace SharpIDE.Application.Features.FileWatching;
public class IdeFileSavedToDiskHandler public class IdeFileSavedToDiskHandler
{ {
private readonly IdeOpenTabsFileManager _openTabsFileManager;
public SharpIdeSolutionModel SolutionModel { get; set; } = null!; public SharpIdeSolutionModel SolutionModel { get; set; } = null!;
public IdeFileSavedToDiskHandler() public IdeFileSavedToDiskHandler(IdeOpenTabsFileManager openTabsFileManager)
{ {
_openTabsFileManager = openTabsFileManager;
GlobalEvents.Instance.IdeFileSavedToDisk.Subscribe(HandleIdeFileChanged); GlobalEvents.Instance.IdeFileSavedToDisk.Subscribe(HandleIdeFileChanged);
} }
@@ -40,11 +42,11 @@ public class IdeFileSavedToDiskHandler
private async Task HandleWorkspaceFileChanged(SharpIdeFile file) private async Task HandleWorkspaceFileChanged(SharpIdeFile file)
{ {
// TODO: Don't reload from disk if we raised the change event ourselves (e.g. save from IDE). Cleanup this whole disaster // TODO: Don't reload from disk if we raised the change event ourselves (e.g. save from IDE). Cleanup this whole disaster
var wasOpenAndUpdated = await IdeOpenTabsFileManager.Instance.ReloadFileFromDiskIfOpenInEditor(file); var wasOpenAndUpdated = await _openTabsFileManager.ReloadFileFromDiskIfOpenInEditor(file);
if (file.IsRoslynWorkspaceFile) if (file.IsRoslynWorkspaceFile)
{ {
var fileText = wasOpenAndUpdated ? var fileText = wasOpenAndUpdated ?
await IdeOpenTabsFileManager.Instance.GetFileTextAsync(file) : await _openTabsFileManager.GetFileTextAsync(file) :
await File.ReadAllTextAsync(file.Path); await File.ReadAllTextAsync(file.Path);
await RoslynAnalysis.UpdateDocument(file, fileText); await RoslynAnalysis.UpdateDocument(file, fileText);
GlobalEvents.Instance.SolutionAltered.InvokeParallelFireAndForget(); GlobalEvents.Instance.SolutionAltered.InvokeParallelFireAndForget();

View File

@@ -0,0 +1,66 @@
using Godot;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using SharpIDE.Application.Features.Build;
using SharpIDE.Application.Features.FilePersistence;
using SharpIDE.Application.Features.FileWatching;
using SharpIDE.Application.Features.Run;
namespace SharpIDE.Godot;
[AttributeUsage(AttributeTargets.Field)]
public class InjectAttribute : Attribute;
public partial class DiAutoload : Node
{
private ServiceProvider? _serviceProvider;
public override void _EnterTree()
{
GD.Print("[Injector] _EnterTree called");
var services = new ServiceCollection();
// Register services here
services.AddSingleton<BuildService>();
services.AddSingleton<RunService>();
services.AddSingleton<IdeFileExternalChangeHandler>();
services.AddSingleton<IdeFileSavedToDiskHandler>();
services.AddSingleton<IdeFileWatcher>();
services.AddSingleton<IdeOpenTabsFileManager>();
_serviceProvider = services.BuildServiceProvider();
GetTree().NodeAdded += OnNodeAdded;
GD.Print("[Injector] Service provider built and NodeAdded event subscribed");
}
public override void _Ready()
{
}
private void OnNodeAdded(Node node)
{
// Inject dependencies into every new node
InjectDependencies(node);
}
private void InjectDependencies(object target)
{
var type = target.GetType();
const BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
foreach (var field in type.GetFields(flags))
{
if (Attribute.IsDefined(field, typeof(InjectAttribute)))
{
var service = _serviceProvider!.GetService(field.FieldType);
if (service is null)
{
GD.PrintErr($"[Injector] No service registered for {field.FieldType}");
GetTree().Quit();
}
field.SetValue(target, service);
}
}
}
}

View File

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

View File

@@ -1,6 +1,7 @@
using System.Threading.Channels; using System.Threading.Channels;
using GDExtensionBindgen; using GDExtensionBindgen;
using Godot; using Godot;
using SharpIDE.Application.Features.Build;
namespace SharpIDE.Godot.Features.Build; namespace SharpIDE.Godot.Features.Build;
@@ -8,10 +9,12 @@ public partial class BuildPanel : Control
{ {
private Terminal _terminal = null!; private Terminal _terminal = null!;
private ChannelReader<string>? _buildOutputChannelReader; private ChannelReader<string>? _buildOutputChannelReader;
[Inject] private readonly BuildService _buildService = null!;
public override void _Ready() public override void _Ready()
{ {
_terminal = new Terminal(GetNode<Control>("%Terminal")); _terminal = new Terminal(GetNode<Control>("%Terminal"));
Singletons.BuildService.BuildStarted += OnBuildStarted; _buildService.BuildStarted += OnBuildStarted;
} }
public override void _Process(double delta) public override void _Process(double delta)
@@ -27,6 +30,6 @@ public partial class BuildPanel : Control
private async Task OnBuildStarted() private async Task OnBuildStarted()
{ {
await this.InvokeAsync(() => _terminal.Clear()); await this.InvokeAsync(() => _terminal.Clear());
_buildOutputChannelReader ??= Singletons.BuildService.BuildTextWriter.ConsoleChannel.Reader; _buildOutputChannelReader ??= _buildService.BuildTextWriter.ConsoleChannel.Reader;
} }
} }

View File

@@ -4,6 +4,7 @@ using R3;
using SharpIDE.Application.Features.Analysis; using SharpIDE.Application.Features.Analysis;
using SharpIDE.Application.Features.Debugging; using SharpIDE.Application.Features.Debugging;
using SharpIDE.Application.Features.Events; using SharpIDE.Application.Features.Events;
using SharpIDE.Application.Features.Run;
using SharpIDE.Application.Features.SolutionDiscovery; using SharpIDE.Application.Features.SolutionDiscovery;
using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence;
@@ -18,6 +19,7 @@ public partial class CodeEditorPanel : MarginContainer
private TabContainer _tabContainer = null!; private TabContainer _tabContainer = null!;
private ExecutionStopInfo? _debuggerExecutionStopInfo; private ExecutionStopInfo? _debuggerExecutionStopInfo;
[Inject] private readonly RunService _runService = null!;
public override void _Ready() public override void _Ready()
{ {
_tabContainer = GetNode<TabContainer>("TabContainer"); _tabContainer = GetNode<TabContainer>("TabContainer");
@@ -125,7 +127,7 @@ public partial class CodeEditorPanel : MarginContainer
_debuggerExecutionStopInfo = null; _debuggerExecutionStopInfo = null;
_ = Task.GodotRun(async () => _ = Task.GodotRun(async () =>
{ {
await Singletons.RunService.SendDebuggerStepOver(threadId); await _runService.SendDebuggerStepOver(threadId);
}); });
} }
} }

View File

@@ -9,6 +9,8 @@ using SharpIDE.Application;
using SharpIDE.Application.Features.Analysis; using SharpIDE.Application.Features.Analysis;
using SharpIDE.Application.Features.Debugging; using SharpIDE.Application.Features.Debugging;
using SharpIDE.Application.Features.Events; using SharpIDE.Application.Features.Events;
using SharpIDE.Application.Features.FilePersistence;
using SharpIDE.Application.Features.Run;
using SharpIDE.Application.Features.SolutionDiscovery; using SharpIDE.Application.Features.SolutionDiscovery;
using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence;
using SharpIDE.RazorAccess; using SharpIDE.RazorAccess;
@@ -39,6 +41,10 @@ public partial class SharpIdeCodeEdit : CodeEdit
private ImmutableArray<CodeAction> _currentCodeActionsInPopup = []; private ImmutableArray<CodeAction> _currentCodeActionsInPopup = [];
private bool _fileChangingSuppressBreakpointToggleEvent; private bool _fileChangingSuppressBreakpointToggleEvent;
[Inject] private readonly IdeOpenTabsFileManager _openTabsFileManager = null!;
[Inject] private readonly RunService _runService = null!;
public override void _Ready() public override void _Ready()
{ {
SyntaxHighlighter = _syntaxHighlighter; SyntaxHighlighter = _syntaxHighlighter;
@@ -112,7 +118,7 @@ public partial class SharpIdeCodeEdit : CodeEdit
var lineInt = (int)line; var lineInt = (int)line;
var breakpointAdded = IsLineBreakpointed(lineInt); var breakpointAdded = IsLineBreakpointed(lineInt);
var lineForDebugger = lineInt + 1; // Godot is 0-indexed, Debugging is 1-indexed var lineForDebugger = lineInt + 1; // Godot is 0-indexed, Debugging is 1-indexed
var breakpoints = Singletons.RunService.Breakpoints.GetOrAdd(_currentFile, []); var breakpoints = _runService.Breakpoints.GetOrAdd(_currentFile, []);
if (breakpointAdded) if (breakpointAdded)
{ {
breakpoints.Add(new Breakpoint { Line = lineForDebugger } ); breakpoints.Add(new Breakpoint { Line = lineForDebugger } );
@@ -265,7 +271,7 @@ public partial class SharpIdeCodeEdit : CodeEdit
_ = Task.GodotRun(async () => _ = Task.GodotRun(async () =>
{ {
_currentFile.IsDirty.Value = true; _currentFile.IsDirty.Value = true;
await Singletons.OpenTabsFileManager.UpdateFileTextInMemory(_currentFile, Text); await _openTabsFileManager.UpdateFileTextInMemory(_currentFile, Text);
await _textChangedCts.CancelAsync(); // Currently the below methods throw, TODO Fix with suppress throwing, and handle await _textChangedCts.CancelAsync(); // Currently the below methods throw, TODO Fix with suppress throwing, and handle
_textChangedCts.Dispose(); _textChangedCts.Dispose();
_textChangedCts = new CancellationTokenSource(); _textChangedCts = new CancellationTokenSource();
@@ -298,7 +304,7 @@ public partial class SharpIdeCodeEdit : CodeEdit
// TODO: This can be more efficient - we can just update in memory and proceed with highlighting etc. Save to disk in background. // TODO: This can be more efficient - we can just update in memory and proceed with highlighting etc. Save to disk in background.
foreach (var (affectedFile, updatedText) in affectedFiles) foreach (var (affectedFile, updatedText) in affectedFiles)
{ {
await Singletons.OpenTabsFileManager.UpdateInMemoryIfOpenAndSaveAsync(affectedFile, updatedText); await _openTabsFileManager.UpdateInMemoryIfOpenAndSaveAsync(affectedFile, updatedText);
affectedFile.FileContentsChangedExternally.InvokeParallelFireAndForget(); affectedFile.FileContentsChangedExternally.InvokeParallelFireAndForget();
} }
}); });
@@ -306,7 +312,7 @@ public partial class SharpIdeCodeEdit : CodeEdit
private async Task OnFileChangedExternallyInMemory() private async Task OnFileChangedExternallyInMemory()
{ {
var fileContents = await Singletons.OpenTabsFileManager.GetFileTextAsync(_currentFile); var fileContents = await _openTabsFileManager.GetFileTextAsync(_currentFile);
var syntaxHighlighting = RoslynAnalysis.GetDocumentSyntaxHighlighting(_currentFile); var syntaxHighlighting = RoslynAnalysis.GetDocumentSyntaxHighlighting(_currentFile);
var razorSyntaxHighlighting = RoslynAnalysis.GetRazorDocumentSyntaxHighlighting(_currentFile); var razorSyntaxHighlighting = RoslynAnalysis.GetRazorDocumentSyntaxHighlighting(_currentFile);
var diagnostics = RoslynAnalysis.GetDocumentDiagnostics(_currentFile); var diagnostics = RoslynAnalysis.GetDocumentDiagnostics(_currentFile);
@@ -341,7 +347,7 @@ public partial class SharpIdeCodeEdit : CodeEdit
{ {
await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding); // get off the UI thread await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding); // get off the UI thread
_currentFile = file; _currentFile = file;
var readFileTask = Singletons.OpenTabsFileManager.GetFileTextAsync(file); var readFileTask = _openTabsFileManager.GetFileTextAsync(file);
_currentFile.FileContentsChangedExternally.Subscribe(OnFileChangedExternallyInMemory); _currentFile.FileContentsChangedExternally.Subscribe(OnFileChangedExternallyInMemory);
_currentFile.FileContentsChangedExternallyFromDisk.Subscribe(OnFileChangedExternallyFromDisk); _currentFile.FileContentsChangedExternallyFromDisk.Subscribe(OnFileChangedExternallyFromDisk);
@@ -365,7 +371,7 @@ public partial class SharpIdeCodeEdit : CodeEdit
private async Task OnFileChangedExternallyFromDisk() private async Task OnFileChangedExternallyFromDisk()
{ {
await Singletons.OpenTabsFileManager.ReloadFileFromDisk(_currentFile); await _openTabsFileManager.ReloadFileFromDisk(_currentFile);
await OnFileChangedExternallyInMemory(); await OnFileChangedExternallyInMemory();
} }
@@ -432,14 +438,14 @@ public partial class SharpIdeCodeEdit : CodeEdit
{ {
_ = Task.GodotRun(async () => _ = Task.GodotRun(async () =>
{ {
await Singletons.OpenTabsFileManager.SaveAllOpenFilesAsync(); await _openTabsFileManager.SaveAllOpenFilesAsync();
}); });
} }
else if (@event.IsActionPressed(InputStringNames.SaveFile)) else if (@event.IsActionPressed(InputStringNames.SaveFile))
{ {
_ = Task.GodotRun(async () => _ = Task.GodotRun(async () =>
{ {
await Singletons.OpenTabsFileManager.SaveFileAsync(_currentFile); await _openTabsFileManager.SaveFileAsync(_currentFile);
}); });
} }
} }

View File

@@ -2,6 +2,7 @@ using Ardalis.GuardClauses;
using Godot; using Godot;
using SharpIDE.Application.Features.Debugging; using SharpIDE.Application.Features.Debugging;
using SharpIDE.Application.Features.Events; using SharpIDE.Application.Features.Events;
using SharpIDE.Application.Features.Run;
using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence;
namespace SharpIDE.Godot.Features.Debug_.Tab.SubTabs; namespace SharpIDE.Godot.Features.Debug_.Tab.SubTabs;
@@ -15,6 +16,8 @@ public partial class ThreadsVariablesSubTab : Control
public SharpIdeProjectModel Project { get; set; } = null!; public SharpIdeProjectModel Project { get; set; } = null!;
// private ThreadModel? _selectedThread = null!; // null when not at a stop point // private ThreadModel? _selectedThread = null!; // null when not at a stop point
[Inject] private readonly RunService _runService = null!;
public override void _Ready() public override void _Ready()
{ {
_threadsVboxContainer = GetNode<VBoxContainer>("%ThreadsPanel/VBoxContainer"); _threadsVboxContainer = GetNode<VBoxContainer>("%ThreadsPanel/VBoxContainer");
@@ -26,7 +29,7 @@ public partial class ThreadsVariablesSubTab : Control
private async Task OnDebuggerExecutionStopped(ExecutionStopInfo stopInfo) private async Task OnDebuggerExecutionStopped(ExecutionStopInfo stopInfo)
{ {
var result = await Singletons.RunService.GetInfoAtStopPoint(); var result = await _runService.GetInfoAtStopPoint();
var threadScenes = result.Threads.Select(s => var threadScenes = result.Threads.Select(s =>
{ {
var threadListItem = _threadListItemScene.Instantiate<Control>(); var threadListItem = _threadListItemScene.Instantiate<Control>();

View File

@@ -1,4 +1,5 @@
using Godot; using Godot;
using SharpIDE.Application.Features.Run;
using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence;
using SharpIDE.Godot.Features.BottomPanel; using SharpIDE.Godot.Features.BottomPanel;
@@ -11,6 +12,8 @@ public partial class RunMenuItem : HBoxContainer
private Button _runButton = null!; private Button _runButton = null!;
private Button _debugButton = null!; private Button _debugButton = null!;
private Button _stopButton = null!; private Button _stopButton = null!;
[Inject] private readonly RunService _runService = null!;
public override void _Ready() public override void _Ready()
{ {
_label = GetNode<Label>("Label"); _label = GetNode<Label>("Label");
@@ -47,18 +50,18 @@ public partial class RunMenuItem : HBoxContainer
private async void OnStopButtonPressed() private async void OnStopButtonPressed()
{ {
await Singletons.RunService.CancelRunningProject(Project); await _runService.CancelRunningProject(Project);
} }
private async void OnRunButtonPressed() private async void OnRunButtonPressed()
{ {
GodotGlobalEvents.Instance.BottomPanelTabExternallySelected.InvokeParallelFireAndForget(BottomPanelType.Run); GodotGlobalEvents.Instance.BottomPanelTabExternallySelected.InvokeParallelFireAndForget(BottomPanelType.Run);
await Singletons.RunService.RunProject(Project).ConfigureAwait(false); await _runService.RunProject(Project).ConfigureAwait(false);
} }
private async void OnDebugButtonPressed() private async void OnDebugButtonPressed()
{ {
GodotGlobalEvents.Instance.BottomPanelTabExternallySelected.InvokeParallelFireAndForget(BottomPanelType.Debug); GodotGlobalEvents.Instance.BottomPanelTabExternallySelected.InvokeParallelFireAndForget(BottomPanelType.Debug);
await Singletons.RunService.RunProject(Project, true).ConfigureAwait(false); await _runService.RunProject(Project, true).ConfigureAwait(false);
} }
} }

View File

@@ -1,6 +1,9 @@
using Godot; using Godot;
using SharpIDE.Application.Features.Analysis;
using SharpIDE.Application.Features.Build; using SharpIDE.Application.Features.Build;
using SharpIDE.Application.Features.Evaluation;
using SharpIDE.Application.Features.Events; using SharpIDE.Application.Features.Events;
using SharpIDE.Application.Features.Run;
using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence;
using SharpIDE.Godot.Features.BottomPanel; using SharpIDE.Godot.Features.BottomPanel;
@@ -18,6 +21,9 @@ file enum ProjectContextMenuOptions
public partial class SolutionExplorerPanel public partial class SolutionExplorerPanel
{ {
private Texture2D _runIcon = ResourceLoader.Load<Texture2D>("uid://bkty6563cthj8"); private Texture2D _runIcon = ResourceLoader.Load<Texture2D>("uid://bkty6563cthj8");
[Inject] private readonly BuildService _buildService = null!;
[Inject] private readonly RunService _runService = null!;
private void OpenContextMenuProject(SharpIdeProjectModel project) private void OpenContextMenuProject(SharpIdeProjectModel project)
{ {
var menu = new PopupMenu(); var menu = new PopupMenu();
@@ -38,7 +44,7 @@ public partial class SolutionExplorerPanel
_ = Task.GodotRun(async () => _ = Task.GodotRun(async () =>
{ {
GodotGlobalEvents.Instance.BottomPanelTabExternallySelected.InvokeParallelFireAndForget(BottomPanelType.Run); GodotGlobalEvents.Instance.BottomPanelTabExternallySelected.InvokeParallelFireAndForget(BottomPanelType.Run);
await Singletons.RunService.RunProject(project); await _runService.RunProject(project);
}); });
} }
if (actionId is ProjectContextMenuOptions.Build) if (actionId is ProjectContextMenuOptions.Build)
@@ -63,9 +69,9 @@ public partial class SolutionExplorerPanel
menu.Position = new Vector2I((int)globalMousePosition.X, (int)globalMousePosition.Y); menu.Position = new Vector2I((int)globalMousePosition.X, (int)globalMousePosition.Y);
menu.Popup(); menu.Popup();
} }
private static async Task MsBuildProject(SharpIdeProjectModel project, BuildType buildType) private async Task MsBuildProject(SharpIdeProjectModel project, BuildType buildType)
{ {
GodotGlobalEvents.Instance.BottomPanelTabExternallySelected.InvokeParallelFireAndForget(BottomPanelType.Build); GodotGlobalEvents.Instance.BottomPanelTabExternallySelected.InvokeParallelFireAndForget(BottomPanelType.Build);
await Singletons.BuildService.MsBuildAsync(project.FilePath, buildType); await _buildService.MsBuildAsync(project.FilePath, buildType);
} }
} }

View File

@@ -1,6 +1,4 @@
using Godot; using Godot;
using Microsoft.Build.Locator;
using Microsoft.Extensions.Hosting;
using SharpIDE.Application.Features.Analysis; using SharpIDE.Application.Features.Analysis;
using SharpIDE.Application.Features.Build; using SharpIDE.Application.Features.Build;
using SharpIDE.Application.Features.Events; using SharpIDE.Application.Features.Events;
@@ -40,19 +38,22 @@ public partial class IdeRoot : Control
private readonly PackedScene _runMenuItemScene = ResourceLoader.Load<PackedScene>("res://Features/Run/RunMenuItem.tscn"); private readonly PackedScene _runMenuItemScene = ResourceLoader.Load<PackedScene>("res://Features/Run/RunMenuItem.tscn");
private TaskCompletionSource _nodeReadyTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); private TaskCompletionSource _nodeReadyTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
[Inject] private readonly IdeFileSavedToDiskHandler _savedToDiskHandler = null!;
[Inject] private readonly IdeFileExternalChangeHandler _fileExternalChangeHandler = null!;
[Inject] private readonly IdeFileWatcher _fileWatcher = null!;
[Inject] private readonly BuildService _buildService = null!;
public override void _EnterTree() public override void _EnterTree()
{ {
GodotGlobalEvents.Instance = new GodotGlobalEvents(); GodotGlobalEvents.Instance = new GodotGlobalEvents();
GlobalEvents.Instance = new GlobalEvents(); GlobalEvents.Instance = new GlobalEvents();
BuildService.Instance = new BuildService(); // TODO: Sort out this mess with singletons, especially access across Application services BuildService.Instance = new BuildService(); // TODO: Sort out this mess with singletons, especially access across Application services
IdeOpenTabsFileManager.Instance = new IdeOpenTabsFileManager(); IdeOpenTabsFileManager.Instance = new IdeOpenTabsFileManager();
Singletons.RunService = new RunService(); }
Singletons.BuildService = BuildService.Instance;
Singletons.FileWatcher?.Dispose(); public override void _ExitTree()
Singletons.FileWatcher = new IdeFileWatcher(); {
Singletons.OpenTabsFileManager = IdeOpenTabsFileManager.Instance; _fileWatcher.Dispose();
Singletons.FileExternalChangeHandler = new IdeFileExternalChangeHandler();
Singletons.FileSavedToDiskHandler = new IdeFileSavedToDiskHandler();
} }
public override void _Ready() public override void _Ready()
@@ -94,22 +95,22 @@ public partial class IdeRoot : Control
private async void OnBuildSlnButtonPressed() private async void OnBuildSlnButtonPressed()
{ {
GodotGlobalEvents.Instance.BottomPanelTabExternallySelected.InvokeParallelFireAndForget(BottomPanelType.Build); GodotGlobalEvents.Instance.BottomPanelTabExternallySelected.InvokeParallelFireAndForget(BottomPanelType.Build);
await Singletons.BuildService.MsBuildAsync(_solutionExplorerPanel.SolutionModel.FilePath); await _buildService.MsBuildAsync(_solutionExplorerPanel.SolutionModel.FilePath);
} }
private async void OnRebuildSlnButtonPressed() private async void OnRebuildSlnButtonPressed()
{ {
GodotGlobalEvents.Instance.BottomPanelTabExternallySelected.InvokeParallelFireAndForget(BottomPanelType.Build); GodotGlobalEvents.Instance.BottomPanelTabExternallySelected.InvokeParallelFireAndForget(BottomPanelType.Build);
await Singletons.BuildService.MsBuildAsync(_solutionExplorerPanel.SolutionModel.FilePath, BuildType.Rebuild); await _buildService.MsBuildAsync(_solutionExplorerPanel.SolutionModel.FilePath, BuildType.Rebuild);
} }
private async void OnCleanSlnButtonPressed() private async void OnCleanSlnButtonPressed()
{ {
GodotGlobalEvents.Instance.BottomPanelTabExternallySelected.InvokeParallelFireAndForget(BottomPanelType.Build); GodotGlobalEvents.Instance.BottomPanelTabExternallySelected.InvokeParallelFireAndForget(BottomPanelType.Build);
await Singletons.BuildService.MsBuildAsync(_solutionExplorerPanel.SolutionModel.FilePath, BuildType.Clean); await _buildService.MsBuildAsync(_solutionExplorerPanel.SolutionModel.FilePath, BuildType.Clean);
} }
private async void OnRestoreSlnButtonPressed() private async void OnRestoreSlnButtonPressed()
{ {
GodotGlobalEvents.Instance.BottomPanelTabExternallySelected.InvokeParallelFireAndForget(BottomPanelType.Build); GodotGlobalEvents.Instance.BottomPanelTabExternallySelected.InvokeParallelFireAndForget(BottomPanelType.Build);
await Singletons.BuildService.MsBuildAsync(_solutionExplorerPanel.SolutionModel.FilePath, BuildType.Restore); await _buildService.MsBuildAsync(_solutionExplorerPanel.SolutionModel.FilePath, BuildType.Restore);
} }
private async Task OnSolutionExplorerPanelOnFileSelected(SharpIdeFile file, SharpIdeFileLinePosition? fileLinePosition) private async Task OnSolutionExplorerPanelOnFileSelected(SharpIdeFile file, SharpIdeFileLinePosition? fileLinePosition)
@@ -129,11 +130,11 @@ public partial class IdeRoot : Control
_bottomPanelManager.Solution = solutionModel; _bottomPanelManager.Solution = solutionModel;
_searchWindow.Solution = solutionModel; _searchWindow.Solution = solutionModel;
_searchAllFilesWindow.Solution = solutionModel; _searchAllFilesWindow.Solution = solutionModel;
Singletons.FileExternalChangeHandler.SolutionModel = solutionModel; _fileExternalChangeHandler.SolutionModel = solutionModel;
Singletons.FileSavedToDiskHandler.SolutionModel = solutionModel; _savedToDiskHandler.SolutionModel = solutionModel;
Callable.From(_solutionExplorerPanel.RepopulateTree).CallDeferred(); Callable.From(_solutionExplorerPanel.RepopulateTree).CallDeferred();
RoslynAnalysis.StartSolutionAnalysis(solutionModel); RoslynAnalysis.StartSolutionAnalysis(solutionModel);
Singletons.FileWatcher.StartWatching(solutionModel); _fileWatcher.StartWatching(solutionModel);
var infraProject = solutionModel.AllProjects.SingleOrDefault(s => s.Name == "WebUi"); var infraProject = solutionModel.AllProjects.SingleOrDefault(s => s.Name == "WebUi");
var diFile = infraProject?.Folders.Single(s => s.Name == "Pages").Files.Single(s => s.Name == "TestPage.razor"); var diFile = infraProject?.Folders.Single(s => s.Name == "Pages").Files.Single(s => s.Name == "TestPage.razor");

View File

@@ -2,6 +2,7 @@ using Godot;
using Microsoft.Build.Locator; using Microsoft.Build.Locator;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using SharpIDE.Application.Features.Events; using SharpIDE.Application.Features.Events;
using SharpIDE.Application.Features.FilePersistence;
using SharpIDE.Godot.Features.IdeSettings; using SharpIDE.Godot.Features.IdeSettings;
using SharpIDE.Godot.Features.SlnPicker; using SharpIDE.Godot.Features.SlnPicker;
@@ -20,8 +21,11 @@ public partial class IdeWindow : Control
private IdeRoot? _ideRoot; private IdeRoot? _ideRoot;
private SlnPicker? _slnPicker; private SlnPicker? _slnPicker;
[Inject] private readonly IdeOpenTabsFileManager _openTabsFileManager = null!;
public override void _Ready() public override void _Ready()
{ {
GD.Print("IdeWindow _Ready called");
ResourceLoader.LoadThreadedRequest(SlnPickerScenePath); ResourceLoader.LoadThreadedRequest(SlnPickerScenePath);
ResourceLoader.LoadThreadedRequest(IdeRootScenePath); ResourceLoader.LoadThreadedRequest(IdeRootScenePath);
MSBuildLocator.RegisterDefaults(); MSBuildLocator.RegisterDefaults();
@@ -45,7 +49,7 @@ public partial class IdeWindow : Control
private void OnFocusExited() private void OnFocusExited()
{ {
_ = Task.GodotRun(async () => await Singletons.OpenTabsFileManager.SaveAllOpenFilesAsync()); _ = Task.GodotRun(async () => await _openTabsFileManager.SaveAllOpenFilesAsync());
} }
public void PickSolution(bool fullscreen = false) public void PickSolution(bool fullscreen = false)

View File

@@ -8,11 +8,5 @@ namespace SharpIDE.Godot;
public static class Singletons public static class Singletons
{ {
public static RunService RunService { get; set; } = null!;
public static BuildService BuildService { get; set; } = null!;
public static IdeFileWatcher FileWatcher { get; set; } = null!;
public static IdeOpenTabsFileManager OpenTabsFileManager { get; set; } = null!;
public static IdeFileExternalChangeHandler FileExternalChangeHandler { get; set; } = null!;
public static IdeFileSavedToDiskHandler FileSavedToDiskHandler { get; set; } = null!;
public static AppState AppState { get; set; } = null!; public static AppState AppState { get; set; } = null!;
} }

View File

@@ -19,6 +19,7 @@ config/icon="uid://tpjfx7mo6cwj"
[autoload] [autoload]
DiAutoload="*res://DiAutoload.cs"
FrameProviderDispatcher="*res://addons/R3.Godot/FrameProviderDispatcher.cs" FrameProviderDispatcher="*res://addons/R3.Godot/FrameProviderDispatcher.cs"
ObservableTrackerRuntimeHook="*res://addons/R3.Godot/ObservableTrackerRuntimeHook.cs" ObservableTrackerRuntimeHook="*res://addons/R3.Godot/ObservableTrackerRuntimeHook.cs"