diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..0abb065 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*.cs] +charset = utf-8 +end_of_line = lf +indent_style = space +indent_size = 4 +insert_final_newline = true +csharp_indent_case_contents_when_block = false diff --git a/GodotAddin/GodotDebuggerEngine.cs b/GodotAddin/Debugging/GodotDebuggerEngine.cs similarity index 98% rename from GodotAddin/GodotDebuggerEngine.cs rename to GodotAddin/Debugging/GodotDebuggerEngine.cs index 63d40f1..625e93e 100644 --- a/GodotAddin/GodotDebuggerEngine.cs +++ b/GodotAddin/Debugging/GodotDebuggerEngine.cs @@ -7,7 +7,7 @@ using MonoDevelop.Core.Execution; using MonoDevelop.Debugger; using MonoDevelop.Ide; -namespace GodotAddin +namespace GodotAddin.Debugging { public class GodotDebuggerEngine : DebuggerEngineBackend { diff --git a/GodotAddin/GodotDebuggerSession.cs b/GodotAddin/Debugging/GodotDebuggerSession.cs similarity index 67% rename from GodotAddin/GodotDebuggerSession.cs rename to GodotAddin/Debugging/GodotDebuggerSession.cs index 63a1993..a4eea81 100644 --- a/GodotAddin/GodotDebuggerSession.cs +++ b/GodotAddin/Debugging/GodotDebuggerSession.cs @@ -8,15 +8,18 @@ using System.Threading.Tasks; using Mono.Debugging.Client; using Mono.Debugging.Soft; using GodotAddin.Utils; +using GodotTools.IdeMessaging.Requests; using System.Collections.Generic; +using GodotTools.IdeMessaging; -namespace GodotAddin +namespace GodotAddin.Debugging { public class GodotDebuggerSession : SoftDebuggerSession { private bool _attached; private NetworkStream _godotRemoteDebuggerStream; private GodotExecutionCommand _godotCmd; + private Process _process; public void SendReloadScripts() { @@ -30,7 +33,7 @@ namespace GodotAddin break; case ExecutionType.PlayInEditor: case ExecutionType.Attach: - _godotCmd.GodotIdeClient.SendReloadScripts(); + _godotCmd.GodotIdeClient.SendRequest(new ReloadScriptsRequest()); break; default: throw new NotImplementedException(_godotCmd.ExecutionType.ToString()); @@ -50,13 +53,24 @@ namespace GodotAddin string godotPath = _godotCmd.GodotIdeClient.GodotEditorExecutablePath; - if (string.IsNullOrEmpty(godotPath) || !File.Exists(godotPath)) - return Settings.GodotExecutablePath; + if (!string.IsNullOrEmpty(godotPath) && File.Exists(godotPath)) + { + // If the setting is not yet assigned any value, set it to the currently connected Godot editor path + if (string.IsNullOrEmpty(Settings.GodotExecutablePath)) + Settings.GodotExecutablePath.Value = godotPath; + return godotPath; + } - return godotPath; + return Settings.GodotExecutablePath; } - protected override async void OnRun(DebuggerStartInfo startInfo) + private void EndSessionWithError(string errorMessage) + { + MonoDevelop.Ide.MessageService.ShowError(errorMessage); + EndSession(); + } + + protected override void OnRun(DebuggerStartInfo startInfo) { var godotStartInfo = (GodotDebuggerStartInfo)startInfo; @@ -69,14 +83,24 @@ namespace GodotAddin _attached = false; StartListening(godotStartInfo, out var assignedDebugPort); - string host = "127.0.0.1"; + var godotMessagingClient = _godotCmd.GodotIdeClient; - if (!await _godotCmd.GodotIdeClient.SendPlay(host, assignedDebugPort)) + if (!godotMessagingClient.IsConnected) { - Exit(); + EndSessionWithError("No Godot editor instance connected"); return; } + string host = "127.0.0.1"; + + var playRequest = new DebugPlayRequest { DebuggerHost = host, DebuggerPort = assignedDebugPort }; + _ = godotMessagingClient.SendRequest(playRequest) + .ContinueWith(t => + { + if (t.Result.Status != GodotTools.IdeMessaging.MessageStatus.Ok) + EndSessionWithError($"Received Play response with status: {MessageStatus.Ok}"); + }); + // TODO: Read the editor player stdout and stderr somehow break; @@ -114,7 +138,27 @@ namespace GodotAddin $",address={host}:{assignedDebugPort}" + ",server=n"; - var process = Process.Start(processStartInfo); + _process = new Process { StartInfo = processStartInfo }; + + try + { + if (!_process.Start()) + { + EndSessionWithError("Failed to start Godot process"); + return; + } + } + catch (System.ComponentModel.Win32Exception e) + { + EndSessionWithError($"Failed to start Godot process: {e.Message}"); + return; + } + + if (_process.HasExited) + { + EndSessionWithError($"Godot process exited with code: {_process.ExitCode}"); + return; + } // Listen for StdOut and StdErr @@ -123,8 +167,10 @@ namespace GodotAddin Name = "Godot StandardOutput Reader", IsBackground = true }; - stdOutThread.Start(new ThreadStartArgs { - IsStdErr = false, Stream = process.StandardOutput + stdOutThread.Start(new ThreadStartArgs + { + IsStdErr = false, + Stream = _process.StandardOutput }); var stdErrThread = new Thread(OutputReader) @@ -132,11 +178,15 @@ namespace GodotAddin Name = "Godot StandardError Reader", IsBackground = true }; - stdErrThread.Start(new ThreadStartArgs { - IsStdErr = true, Stream = process.StandardError + stdErrThread.Start(new ThreadStartArgs + { + IsStdErr = true, + Stream = _process.StandardError }); - OnDebuggerOutput(false, $"Godot PID:{process.Id}{Environment.NewLine}"); + _process.Exited += (sender, args) => EndSession(); + + OnDebuggerOutput(false, $"Godot PID:{_process.Id}{Environment.NewLine}"); break; } @@ -161,6 +211,7 @@ namespace GodotAddin // There is no library to decode this messages, so // we just pump buffer so it doesn't go out of memory var readBytes = await _godotRemoteDebuggerStream.ReadAsync(buffer, 0, buffer.Length); + _ = readBytes; } } @@ -178,9 +229,16 @@ namespace GodotAddin protected override void OnExit() { if (_attached) + { base.OnDetach(); + } else + { base.OnExit(); + + if (_process != null && !_process.HasExited) + _process.Kill(); + } } private void OutputReader(object args) diff --git a/GodotAddin/GodotDebuggerStartInfo.cs b/GodotAddin/Debugging/GodotDebuggerStartInfo.cs similarity index 92% rename from GodotAddin/GodotDebuggerStartInfo.cs rename to GodotAddin/Debugging/GodotDebuggerStartInfo.cs index adea7d3..013a543 100644 --- a/GodotAddin/GodotDebuggerStartInfo.cs +++ b/GodotAddin/Debugging/GodotDebuggerStartInfo.cs @@ -1,6 +1,6 @@ using Mono.Debugging.Soft; -namespace GodotAddin +namespace GodotAddin.Debugging { public class GodotDebuggerStartInfo : SoftDebuggerStartInfo { diff --git a/GodotAddin/GodotExecutionCommand.cs b/GodotAddin/Debugging/GodotExecutionCommand.cs similarity index 74% rename from GodotAddin/GodotExecutionCommand.cs rename to GodotAddin/Debugging/GodotExecutionCommand.cs index 6d7d9a7..ba9dce4 100644 --- a/GodotAddin/GodotExecutionCommand.cs +++ b/GodotAddin/Debugging/GodotExecutionCommand.cs @@ -1,11 +1,12 @@ -using MonoDevelop.Core.Execution; +using GodotTools.IdeMessaging; +using MonoDevelop.Core.Execution; -namespace GodotAddin +namespace GodotAddin.Debugging { public class GodotExecutionCommand : ExecutionCommand { public GodotExecutionCommand(string godotProjectPath, ExecutionType executionType, - string workingDirectory, GodotMonoDevelopClient godotIdeClient) + string workingDirectory, Client godotIdeClient) { GodotProjectPath = godotProjectPath; ExecutionType = executionType; @@ -16,7 +17,7 @@ namespace GodotAddin public string GodotProjectPath { get; } public ExecutionType ExecutionType { get; } public string WorkingDirectory { get; } - public GodotMonoDevelopClient GodotIdeClient { get; } + public Client GodotIdeClient { get; } } public enum ExecutionType diff --git a/GodotAddin/GodotSoftDebuggerArgs.cs b/GodotAddin/Debugging/GodotSoftDebuggerArgs.cs similarity index 92% rename from GodotAddin/GodotSoftDebuggerArgs.cs rename to GodotAddin/Debugging/GodotSoftDebuggerArgs.cs index 82a67fe..400417a 100644 --- a/GodotAddin/GodotSoftDebuggerArgs.cs +++ b/GodotAddin/Debugging/GodotSoftDebuggerArgs.cs @@ -1,7 +1,7 @@ using System.Net; using Mono.Debugging.Soft; -namespace GodotAddin +namespace GodotAddin.Debugging { public class GodotSoftDebuggerArgs : SoftDebuggerRemoteArgs { diff --git a/GodotAddin/GodotAddin.csproj b/GodotAddin/GodotAddin.csproj index 702078c..61768d5 100644 --- a/GodotAddin/GodotAddin.csproj +++ b/GodotAddin/GodotAddin.csproj @@ -1,6 +1,11 @@ - net471 + net472 + 8.0 + + + + @@ -8,6 +13,13 @@ - + - + + + + + + + + \ No newline at end of file diff --git a/GodotAddin/GodotMessaging/MessageHandler.cs b/GodotAddin/GodotMessaging/MessageHandler.cs new file mode 100644 index 0000000..fc853ef --- /dev/null +++ b/GodotAddin/GodotMessaging/MessageHandler.cs @@ -0,0 +1,40 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using GodotTools.IdeMessaging; +using GodotTools.IdeMessaging.Requests; +using MonoDevelop.Core; +using MonoDevelop.Ide; +using MonoDevelop.Ide.Gui; + +namespace GodotAddin.GodotMessaging +{ + public class MessageHandler : ClientMessageHandler + { + private static void DispatchToGuiThread(Action action) + { + var d = new SendOrPostCallback((target) => action()); + DispatchService.SynchronizationContext.Send(d, null); + } + + protected override Task HandleOpenFile(OpenFileRequest request) + { + DispatchToGuiThread(() => + { + var fileOpenInfo = new FileOpenInformation(new FilePath(request.File), + project: null /* autodetect */, + line: request.Line ?? 0, + column: request.Column ?? 0, + options: OpenDocumentOptions.Default + ); + + IdeApp.OpenFiles(new[] { fileOpenInfo }); + + // Make the Ide window grab focus + IdeApp.Workbench.Present(); + }); + + return Task.FromResult(new OpenFileResponse { Status = MessageStatus.Ok }); + } + } +} diff --git a/GodotAddin/GodotMonoDevelopClient.cs b/GodotAddin/GodotMonoDevelopClient.cs deleted file mode 100644 index 14b0003..0000000 --- a/GodotAddin/GodotMonoDevelopClient.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; -using GodotTools.IdeMessaging; -using MonoDevelop.Core; -using MonoDevelop.Ide; -using MonoDevelop.Ide.Gui; - -namespace GodotAddin -{ - public class GodotMonoDevelopClient : DefaultClient - { - private class MonoDevelopLogger : ILogger - { - public void LogDebug(string message) - { - LoggingService.LogDebug(message); - } - - public void LogInfo(string message) - { - LoggingService.LogInfo(message); - } - - public void LogWarning(string message) - { - LoggingService.LogWarning(message); - } - - public void LogError(string message) - { - LoggingService.LogError(message); - } - - public void LogError(string message, Exception e) - { - LoggingService.LogError(message, e); - } - } - - private static string DetermineIdentity() => // TODO: Proper detection of whether we are running on VSMac or MD - RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "VisualStudioForMac" : "MonoDevelop"; - - public GodotMonoDevelopClient(string godotProjectDir) - : base(DetermineIdentity(), godotProjectDir, new MonoDevelopLogger()) - { - } - - public string GodotEditorExecutablePath => GodotIdeMetadata.EditorExecutablePath; - - private static void DispatchToGuiThread(Action action) - { - var d = new SendOrPostCallback((target) => action()); - DispatchService.SynchronizationContext.Send(d, null); - } - - protected override async Task OpenFile(string file) - { - await OpenFile(file, line: 0, column: 0); - } - - protected override async Task OpenFile(string file, int line) - { - await OpenFile(file, line, column: 0); - } - - protected override Task OpenFile(string file, int line, int column) - { - DispatchToGuiThread(() => - { - var fileOpenInfo = new FileOpenInformation(new FilePath(file), - project: null /* autodetect */, - line: line, - column: column, - options: OpenDocumentOptions.Default - ); - - IdeApp.OpenFiles(new[] {fileOpenInfo}); - - // Make the Ide window grab focus - IdeApp.Workbench.Present(); - }); - return Task.CompletedTask; - } - - public Task SendPlay() - { - return WriteMessage(new Message("Play")); - } - - public Task SendPlay(string debuggerHost, int debuggerPort) - { - return WriteMessage(new Message("Play", debuggerHost, debuggerPort.ToString())); - } - - public Task SendReloadScripts() - { - return WriteMessage(new Message("ReloadScripts")); - } - } -} diff --git a/GodotAddin/GodotOptionsPanel.cs b/GodotAddin/GodotOptionsPanel.cs index 8dfd0c3..582f629 100644 --- a/GodotAddin/GodotOptionsPanel.cs +++ b/GodotAddin/GodotOptionsPanel.cs @@ -6,49 +6,89 @@ namespace GodotAddin { public class GodotOptionsPanel : OptionsPanel { - private readonly FileEntry _godotExeFileEntry = new FileEntry(); - private readonly Gtk.CheckButton _alwaysUseExeCheckButton = new Gtk.CheckButton(); + private readonly FileEntry _godotExeFile = new FileEntry(); + private readonly Gtk.CheckButton _alwaysUseExe = new Gtk.CheckButton { BorderWidth = 10 }; + + private readonly Gtk.CheckButton _provideNodePathCompletion = new Gtk.CheckButton { BorderWidth = 10 }; + private readonly Gtk.CheckButton _provideInputActionCompletion = new Gtk.CheckButton { BorderWidth = 10 }; + private readonly Gtk.CheckButton _provideResourcePathCompletion = new Gtk.CheckButton { BorderWidth = 10 }; + private readonly Gtk.CheckButton _provideScenePathCompletion = new Gtk.CheckButton { BorderWidth = 10 }; + private readonly Gtk.CheckButton _provideSignalNameCompletion = new Gtk.CheckButton { BorderWidth = 10 }; + + private void AddSection(Gtk.VBox vbox, string text) + { + var hbox = new Gtk.HBox(); + + var sectionLabel = new Gtk.Label + { + Text = $"{GettextCatalog.GetString(text)}", + UseMarkup = true, + Xalign = 0 + }; + + hbox.PackStart(sectionLabel, false, false, 0); + + vbox.PackStart(hbox, false, false, 0); + } + + private static void AddFileProperty(Gtk.VBox vbox, string labelText, FileEntry fileEntry, ConfigurationProperty property) + { + var alignment = new Gtk.Alignment(0f, 0f, 1f, 1f) { LeftPadding = 24 }; + + var innerVBox = new Gtk.VBox(); + alignment.Add(innerVBox); + + var hbox = new Gtk.HBox(false, 6); + + var label = new Gtk.Label + { + Text = GettextCatalog.GetString(labelText), + Xalign = 0 + }; + + hbox.PackStart(label, false, false, 0); + fileEntry.Path = property.Value; + hbox.PackStart(fileEntry, true, true, 0); + + innerVBox.PackStart(hbox, false, false, 0); + + vbox.PackStart(alignment, false, false, 0); + } + + private static void AddCheckProperty(Gtk.VBox vbox, string labelText, Gtk.CheckButton checkButton, ConfigurationProperty property) + { + var hbox = new Gtk.HBox(); + + checkButton.Active = property.Value; + hbox.PackStart(checkButton, false, false, 0); + + var label = new Gtk.Label + { + Text = GettextCatalog.GetString(labelText), + Xalign = 0 + }; + + hbox.PackStart(label, true, true, 0); + + vbox.PackStart(hbox, false, false, 0); + } public override Control CreatePanelWidget() { var vbox = new Gtk.VBox { Spacing = 6 }; - var generalSectionLabel = new Gtk.Label - { - Text = $"{GettextCatalog.GetString("General")}", - UseMarkup = true, - Xalign = 0 - }; + AddSection(vbox, "Debugging"); - vbox.PackStart(generalSectionLabel, false, false, 0); + AddCheckProperty(vbox, "Always use this executable.", _alwaysUseExe, Settings.AlwaysUseConfiguredExecutable); + AddFileProperty(vbox, "Godot executable:", _godotExeFile, Settings.GodotExecutablePath); - var godotExeHBox = new Gtk.HBox { BorderWidth = 10, Spacing = 6 }; + AddSection(vbox, "Code completion"); - var godotExeLabel = new Gtk.Label - { - Text = GettextCatalog.GetString("Godot executable:"), - Xalign = 0 - }; - - godotExeHBox.PackStart(godotExeLabel, false, false, 0); - _godotExeFileEntry.Path = Settings.GodotExecutablePath.Value; - godotExeHBox.PackStart(_godotExeFileEntry, true, true, 0); - - vbox.PackStart(godotExeHBox, false, false, 0); - - var alwaysUseExeHBox = new Gtk.HBox { BorderWidth = 10, Spacing = 6 }; - - var alwaysUseExeLabel = new Gtk.Label - { - Text = GettextCatalog.GetString("Always use this executable:"), - Xalign = 0 - }; - - alwaysUseExeHBox.PackStart(alwaysUseExeLabel, false, false, 0); - _alwaysUseExeCheckButton.Active = Settings.AlwaysUseConfiguredExecutable.Value; - alwaysUseExeHBox.PackStart(_alwaysUseExeCheckButton, true, true, 0); - - vbox.PackStart(alwaysUseExeHBox, false, false, 0); + AddCheckProperty(vbox, "Provide node path completions.", _provideNodePathCompletion, Settings.ProvideNodePathCompletions); + AddCheckProperty(vbox, "Provide input action completions.", _provideInputActionCompletion, Settings.ProvideInputActionCompletions); + AddCheckProperty(vbox, "Provide resource path completions.", _provideResourcePathCompletion, Settings.ProvideResourcePathCompletions); + AddCheckProperty(vbox, "Provide scene path completions.", _provideScenePathCompletion, Settings.ProvideScenePathCompletions); + AddCheckProperty(vbox, "Provide signal name completions.", _provideSignalNameCompletion, Settings.ProvideSignalNameCompletions); vbox.ShowAll(); @@ -57,8 +97,14 @@ namespace GodotAddin public override void ApplyChanges() { - Settings.GodotExecutablePath.Value = _godotExeFileEntry.Path; - Settings.GodotExecutablePath.Value = _godotExeFileEntry.Path; + Settings.GodotExecutablePath.Value = _godotExeFile.Path; + Settings.AlwaysUseConfiguredExecutable.Value = _alwaysUseExe.Active; + + Settings.ProvideNodePathCompletions.Value = _provideNodePathCompletion.Active; + Settings.ProvideInputActionCompletions.Value = _provideInputActionCompletion.Active; + Settings.ProvideResourcePathCompletions.Value = _provideResourcePathCompletion.Active; + Settings.ProvideScenePathCompletions.Value = _provideScenePathCompletion.Active; + Settings.ProvideSignalNameCompletions.Value = _provideSignalNameCompletion.Active; } } } diff --git a/GodotAddin/GodotProjectExtension.cs b/GodotAddin/GodotProjectExtension.cs index 06b911b..bdda0f7 100644 --- a/GodotAddin/GodotProjectExtension.cs +++ b/GodotAddin/GodotProjectExtension.cs @@ -1,7 +1,12 @@ using System; using System.Collections.Generic; using System.IO; +using System.Runtime.InteropServices; using System.Threading.Tasks; +using GodotAddin.Debugging; +using GodotAddin.GodotMessaging; +using GodotCompletionProviders; +using GodotTools.IdeMessaging; using MonoDevelop.Core; using MonoDevelop.Core.Execution; using MonoDevelop.Ide; @@ -26,6 +31,9 @@ namespace GodotAddin ExecutionType.Attach }; + public Client GodotMessagingClient { get; private set; } + public MonoDevelopLogger Logger { get; } = new MonoDevelopLogger(); + private static SolutionItemRunConfiguration GetRunConfiguration(ExecutionType type) { switch (type) @@ -41,7 +49,16 @@ namespace GodotAddin } } - private GodotMonoDevelopClient _godotIdeClient; + private void OnClientConnected() + { + // If the setting is not yet assigned any value, set it to the currently connected Godot editor path + if (string.IsNullOrEmpty(Settings.GodotExecutablePath)) + { + string godotPath = GodotMessagingClient?.GodotEditorExecutablePath; + if (!string.IsNullOrEmpty(godotPath) && File.Exists(godotPath)) + Settings.GodotExecutablePath.Value = godotPath; + } + } protected override void OnItemReady() { @@ -55,9 +72,15 @@ namespace GodotAddin try { - _godotIdeClient?.Dispose(); - _godotIdeClient = new GodotMonoDevelopClient(godotProjectDir); - _godotIdeClient.Start(); + string DetermineIdentity() => // TODO: Proper detection of whether we are running on VSMac or MD + RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "VisualStudioForMac" : "MonoDevelop"; + + GodotMessagingClient?.Dispose(); + GodotMessagingClient = new Client(DetermineIdentity(), godotProjectDir, new MessageHandler(), new MonoDevelopLogger()); + GodotMessagingClient.Connected += OnClientConnected; + GodotMessagingClient.Start(); + + BaseCompletionProvider.Context = new GodotProviderContext(this); } catch (Exception e) { @@ -84,7 +107,7 @@ namespace GodotAddin var executionType = ExecutionTypes[runConfigurationIndex]; - if (executionType == ExecutionType.PlayInEditor && !_godotIdeClient.IsConnected) + if (executionType == ExecutionType.PlayInEditor && !GodotMessagingClient.IsConnected) LoggingService.LogError($"Cannot launch editor player because the Godot Ide Client is not connected"); string godotProjectPath = GetGodotProjectPath(); @@ -93,7 +116,7 @@ namespace GodotAddin godotProjectPath, executionType, Path.GetDirectoryName(godotProjectPath), - _godotIdeClient + GodotMessagingClient ); } @@ -128,9 +151,9 @@ namespace GodotAddin { if (runConfiguration == GetRunConfiguration(ExecutionType.PlayInEditor)) { - // 'Play in Editor' requires the Godot Ide Client to be connected - // to a server and the selected run configuration to be 'Debug'. - if (!_godotIdeClient.IsConnected || IdeApp.Workspace.ActiveConfigurationId != "Debug") + // 'Play in Editor' requires the Godot Ide Client to be connected to the server and + // the selected run configuration to be 'Debug' (editor/editor player configuration). + if (!GodotMessagingClient.IsConnected || IdeApp.Workspace.ActiveConfigurationId != "Debug") return false; } @@ -155,6 +178,7 @@ namespace GodotAddin } } } + await base.OnExecuteCommand(monitor, context, configuration, executionCommand); } @@ -162,7 +186,7 @@ namespace GodotAddin { base.Dispose(); - _godotIdeClient?.Dispose(); + GodotMessagingClient?.Dispose(); } } } diff --git a/GodotAddin/GodotProviderContext.cs b/GodotAddin/GodotProviderContext.cs new file mode 100644 index 0000000..12753cb --- /dev/null +++ b/GodotAddin/GodotProviderContext.cs @@ -0,0 +1,57 @@ +using System; +using System.Threading.Tasks; +using GodotCompletionProviders; +using GodotTools.IdeMessaging; +using GodotTools.IdeMessaging.Requests; +using ILogger = GodotCompletionProviders.ILogger; + +namespace GodotAddin +{ + internal class GodotProviderContext : IProviderContext + { + private readonly GodotProjectExtension _extension; + + public GodotProviderContext(GodotProjectExtension package) + { + _extension = package; + } + + public ILogger GetLogger() => _extension.Logger; + + public bool AreCompletionsEnabledFor(CompletionKind completionKind) + { + return completionKind switch + { + CompletionKind.NodePaths => Settings.ProvideNodePathCompletions, + CompletionKind.InputActions => Settings.ProvideInputActionCompletions, + CompletionKind.ResourcePaths => Settings.ProvideResourcePathCompletions, + CompletionKind.ScenePaths => Settings.ProvideScenePathCompletions, + CompletionKind.Signals => Settings.ProvideSignalNameCompletions, + _ => false + }; + } + + public bool CanRequestCompletionsFromServer() + { + var godotMessagingClient = _extension.GodotMessagingClient; + return godotMessagingClient != null && godotMessagingClient.IsConnected; + } + + public async Task RequestCompletion(CompletionKind completionKind, string absoluteFilePath) + { + var godotMessagingClient = _extension.GodotMessagingClient; + + if (godotMessagingClient == null) + throw new InvalidOperationException(); + + var request = new CodeCompletionRequest { Kind = (CodeCompletionRequest.CompletionKind)completionKind, ScriptFile = absoluteFilePath }; + var response = await godotMessagingClient.SendRequest(request); + + if (response.Status == MessageStatus.Ok) + return response.Suggestions; + + GetLogger().LogError($"Received code completion response with status '{response.Status}'."); + return new string[] { }; + } + } +} diff --git a/GodotAddin/MonoDevelopLogger.cs b/GodotAddin/MonoDevelopLogger.cs new file mode 100644 index 0000000..bbdfce1 --- /dev/null +++ b/GodotAddin/MonoDevelopLogger.cs @@ -0,0 +1,33 @@ +using System; +using MonoDevelop.Core; + +namespace GodotAddin +{ + public class MonoDevelopLogger : GodotTools.IdeMessaging.ILogger, GodotCompletionProviders.ILogger + { + public void LogDebug(string message) + { + LoggingService.LogDebug(message); + } + + public void LogInfo(string message) + { + LoggingService.LogInfo(message); + } + + public void LogWarning(string message) + { + LoggingService.LogWarning(message); + } + + public void LogError(string message) + { + LoggingService.LogError(message); + } + + public void LogError(string message, Exception e) + { + LoggingService.LogError(message, e); + } + } +} diff --git a/GodotAddin/Properties/Manifest.addin.xml b/GodotAddin/Properties/Manifest.addin.xml index 8ecfec9..0422056 100644 --- a/GodotAddin/Properties/Manifest.addin.xml +++ b/GodotAddin/Properties/Manifest.addin.xml @@ -5,11 +5,14 @@ id="MonoDevelop.MicroFramework.Debugger" name="Debugger for Godot" features="Breakpoints, Pause, Stepping, DebugFile, ConditionalBreakpoints, Tracepoints, Catchpoints, Disassembly" - type="GodotAddin.GodotDebuggerEngine" /> + type="GodotAddin.Debugging.GodotDebuggerEngine" /> -
+
+ + + diff --git a/GodotAddin/Settings.cs b/GodotAddin/Settings.cs index b698580..315a852 100644 --- a/GodotAddin/Settings.cs +++ b/GodotAddin/Settings.cs @@ -21,10 +21,25 @@ namespace GodotAddin return string.Empty; } - public static readonly ConfigurationProperty GodotExecutablePath = - ConfigurationProperty.Create("Godot.GodotExecutable", DetermineDefaultGodotPath()); - public static readonly ConfigurationProperty AlwaysUseConfiguredExecutable = - ConfigurationProperty.Create("Godot.AlwaysUseConfiguredExecutable", false); + ConfigurationProperty.Create("Godot.Debugging.AlwaysUseConfiguredExecutable", false); + + public static readonly ConfigurationProperty GodotExecutablePath = + ConfigurationProperty.Create("Godot.Debugging.GodotExecutable", DetermineDefaultGodotPath()); + + public static ConfigurationProperty ProvideNodePathCompletions { get; set; } = + ConfigurationProperty.Create("Godot.CodeCompletion.ProvideNodePathCompletions", true); + + public static ConfigurationProperty ProvideInputActionCompletions { get; set; } = + ConfigurationProperty.Create("Godot.CodeCompletion.ProvideInputActionCompletions", true); + + public static ConfigurationProperty ProvideResourcePathCompletions { get; set; } = + ConfigurationProperty.Create("Godot.CodeCompletion.ProvideResourcePathCompletions", true); + + public static ConfigurationProperty ProvideScenePathCompletions { get; set; } = + ConfigurationProperty.Create("Godot.CodeCompletion.ProvideScenePathCompletions", true); + + public static ConfigurationProperty ProvideSignalNameCompletions { get; set; } = + ConfigurationProperty.Create("Godot.CodeCompletion.ProvideSignalNameCompletions", true); } }