mirror of
https://github.com/godotengine/godot-monodevelop-addin.git
synced 2025-12-31 17:48:12 +03:00
Update Messaging library and add Godot completion providers
This commit is contained in:
9
.editorconfig
Normal file
9
.editorconfig
Normal file
@@ -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
|
||||
@@ -7,7 +7,7 @@ using MonoDevelop.Core.Execution;
|
||||
using MonoDevelop.Debugger;
|
||||
using MonoDevelop.Ide;
|
||||
|
||||
namespace GodotAddin
|
||||
namespace GodotAddin.Debugging
|
||||
{
|
||||
public class GodotDebuggerEngine : DebuggerEngineBackend
|
||||
{
|
||||
@@ -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<ReloadScriptsResponse>(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<DebugPlayResponse>(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)
|
||||
@@ -1,6 +1,6 @@
|
||||
using Mono.Debugging.Soft;
|
||||
|
||||
namespace GodotAddin
|
||||
namespace GodotAddin.Debugging
|
||||
{
|
||||
public class GodotDebuggerStartInfo : SoftDebuggerStartInfo
|
||||
{
|
||||
@@ -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
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Net;
|
||||
using Mono.Debugging.Soft;
|
||||
|
||||
namespace GodotAddin
|
||||
namespace GodotAddin.Debugging
|
||||
{
|
||||
public class GodotSoftDebuggerArgs : SoftDebuggerRemoteArgs
|
||||
{
|
||||
@@ -1,6 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net471</TargetFramework>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<LangVersion>8.0</LangVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<AddinReference Include="MonoDevelop.Debugger" />
|
||||
@@ -8,6 +13,13 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MonoDevelop.Addins" Version="0.4.7" />
|
||||
<PackageReference Include="GodotTools.IdeMessaging" Version="1.0.*" />
|
||||
<PackageReference Include="GodotTools.IdeMessaging" Version="1.1.1" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
<ItemGroup>
|
||||
<Folder Include="GodotMessaging\" />
|
||||
<Folder Include="Debugging\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="GodotCompletionProviders" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
40
GodotAddin/GodotMessaging/MessageHandler.cs
Normal file
40
GodotAddin/GodotMessaging/MessageHandler.cs
Normal file
@@ -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<Response> 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<Response>(new OpenFileResponse { Status = MessageStatus.Ok });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<bool> SendPlay()
|
||||
{
|
||||
return WriteMessage(new Message("Play"));
|
||||
}
|
||||
|
||||
public Task<bool> SendPlay(string debuggerHost, int debuggerPort)
|
||||
{
|
||||
return WriteMessage(new Message("Play", debuggerHost, debuggerPort.ToString()));
|
||||
}
|
||||
|
||||
public Task<bool> SendReloadScripts()
|
||||
{
|
||||
return WriteMessage(new Message("ReloadScripts"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 = $"<b>{GettextCatalog.GetString(text)}</b>",
|
||||
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<string> 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<bool> 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 = $"<b>{GettextCatalog.GetString("General")}</b>",
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
57
GodotAddin/GodotProviderContext.cs
Normal file
57
GodotAddin/GodotProviderContext.cs
Normal file
@@ -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<string[]> 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<CodeCompletionResponse>(request);
|
||||
|
||||
if (response.Status == MessageStatus.Ok)
|
||||
return response.Suggestions;
|
||||
|
||||
GetLogger().LogError($"Received code completion response with status '{response.Status}'.");
|
||||
return new string[] { };
|
||||
}
|
||||
}
|
||||
}
|
||||
33
GodotAddin/MonoDevelopLogger.cs
Normal file
33
GodotAddin/MonoDevelopLogger.cs
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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" />
|
||||
</Extension>
|
||||
<Extension path="/MonoDevelop/Ide/GlobalOptionsDialog">
|
||||
<Section id="Godot" _label="Godot Engine" insertafter="VersionControl">
|
||||
<Section id="Godot" _label="Godot" insertafter="VersionControl">
|
||||
<Section id="GodotGeneral" _label="General" fill="true" class="GodotAddin.GodotOptionsPanel" />
|
||||
</Section>
|
||||
</Extension>
|
||||
<Extension path="/MonoDevelop/Ide/TypeService/MefHostServices">
|
||||
<Assembly file="GodotCompletionProviders.dll"/>
|
||||
</Extension>
|
||||
</ExtensionModel>
|
||||
|
||||
@@ -21,10 +21,25 @@ namespace GodotAddin
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public static readonly ConfigurationProperty<string> GodotExecutablePath =
|
||||
ConfigurationProperty.Create("Godot.GodotExecutable", DetermineDefaultGodotPath());
|
||||
|
||||
public static readonly ConfigurationProperty<bool> AlwaysUseConfiguredExecutable =
|
||||
ConfigurationProperty.Create("Godot.AlwaysUseConfiguredExecutable", false);
|
||||
ConfigurationProperty.Create("Godot.Debugging.AlwaysUseConfiguredExecutable", false);
|
||||
|
||||
public static readonly ConfigurationProperty<string> GodotExecutablePath =
|
||||
ConfigurationProperty.Create("Godot.Debugging.GodotExecutable", DetermineDefaultGodotPath());
|
||||
|
||||
public static ConfigurationProperty<bool> ProvideNodePathCompletions { get; set; } =
|
||||
ConfigurationProperty.Create("Godot.CodeCompletion.ProvideNodePathCompletions", true);
|
||||
|
||||
public static ConfigurationProperty<bool> ProvideInputActionCompletions { get; set; } =
|
||||
ConfigurationProperty.Create("Godot.CodeCompletion.ProvideInputActionCompletions", true);
|
||||
|
||||
public static ConfigurationProperty<bool> ProvideResourcePathCompletions { get; set; } =
|
||||
ConfigurationProperty.Create("Godot.CodeCompletion.ProvideResourcePathCompletions", true);
|
||||
|
||||
public static ConfigurationProperty<bool> ProvideScenePathCompletions { get; set; } =
|
||||
ConfigurationProperty.Create("Godot.CodeCompletion.ProvideScenePathCompletions", true);
|
||||
|
||||
public static ConfigurationProperty<bool> ProvideSignalNameCompletions { get; set; } =
|
||||
ConfigurationProperty.Create("Godot.CodeCompletion.ProvideSignalNameCompletions", true);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user