Browse Source

Mono: Fix opening code editors in OSX and cleanup

Ignacio Etcheverry 7 years ago
parent
commit
50f6dbff87

+ 96 - 9
modules/mono/editor/GodotSharpTools/Editor/MonoDevelopInstance.cs

@@ -2,13 +2,23 @@ using System;
 using System.IO;
 using System.IO;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
 
 
 namespace GodotSharpTools.Editor
 namespace GodotSharpTools.Editor
 {
 {
     public class MonoDevelopInstance
     public class MonoDevelopInstance
     {
     {
-        private Process process;
-        private string solutionFile;
+        public enum EditorId
+        {
+            MonoDevelop = 0,
+            VisualStudioForMac = 1
+        }
+
+        readonly string solutionFile;
+        readonly EditorId editorId;
+
+        Process process;
 
 
         public void Execute(string[] files)
         public void Execute(string[] files)
         {
         {
@@ -16,6 +26,35 @@ namespace GodotSharpTools.Editor
 
 
             List<string> args = new List<string>();
             List<string> args = new List<string>();
 
 
+            string command;
+
+            if (Utils.OS.IsOSX())
+            {
+                string bundleId = codeEditorBundleIds[editorId];
+
+                if (IsApplicationBundleInstalled(bundleId))
+                {
+                    command = "open";
+
+                    args.Add("-b");
+                    args.Add(bundleId);
+
+                    // The 'open' process must wait until the application finishes
+                    if (newWindow)
+                        args.Add("--wait-apps");
+
+                    args.Add("--args");
+                }
+                else
+                {
+                    command = codeEditorPaths[editorId];
+                }
+            }
+            else
+            {
+                command = codeEditorPaths[editorId];
+            }
+
             args.Add("--ipc-tcp");
             args.Add("--ipc-tcp");
 
 
             if (newWindow)
             if (newWindow)
@@ -33,25 +72,73 @@ namespace GodotSharpTools.Editor
 
 
             if (newWindow)
             if (newWindow)
             {
             {
-                ProcessStartInfo startInfo = new ProcessStartInfo(MonoDevelopFile, string.Join(" ", args));
-                process = Process.Start(startInfo);
+                process = Process.Start(new ProcessStartInfo()
+                {
+                    FileName = command,
+                    Arguments = string.Join(" ", args),
+                    UseShellExecute = false
+                });
             }
             }
             else
             else
             {
             {
-                Process.Start(MonoDevelopFile, string.Join(" ", args));
+                Process.Start(new ProcessStartInfo()
+                {
+                    FileName = command,
+                    Arguments = string.Join(" ", args),
+                    UseShellExecute = false
+                });
             }
             }
         }
         }
 
 
-        public MonoDevelopInstance(string solutionFile)
+        public MonoDevelopInstance(string solutionFile, EditorId editorId)
         {
         {
+            if (editorId == EditorId.VisualStudioForMac && !Utils.OS.IsOSX())
+                throw new InvalidOperationException($"{nameof(EditorId.VisualStudioForMac)} not supported on this platform");
+
             this.solutionFile = solutionFile;
             this.solutionFile = solutionFile;
+            this.editorId = editorId;
         }
         }
 
 
-        private static string MonoDevelopFile
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private extern static bool IsApplicationBundleInstalled(string bundleId);
+
+        static readonly IReadOnlyDictionary<EditorId, string> codeEditorPaths;
+        static readonly IReadOnlyDictionary<EditorId, string> codeEditorBundleIds;
+
+        static MonoDevelopInstance()
         {
         {
-            get
+            if (Utils.OS.IsOSX())
+            {
+                codeEditorPaths = new Dictionary<EditorId, string>
+                {
+                    // Rely on PATH
+                    { EditorId.MonoDevelop, "monodevelop" },
+                    { EditorId.VisualStudioForMac, "VisualStudio" }
+                };
+                codeEditorBundleIds = new Dictionary<EditorId, string>
+                {
+                    // TODO EditorId.MonoDevelop
+                    { EditorId.VisualStudioForMac, "com.microsoft.visual-studio" }
+                };
+            }
+            else if (Utils.OS.IsWindows())
+            {
+                codeEditorPaths = new Dictionary<EditorId, string>
+                {
+                    // XamarinStudio is no longer a thing, and the latest version is quite old
+                    // MonoDevelop is available from source only on Windows. The recommendation
+                    // is to use Visual Studio instead. Since there are no official builds, we
+                    // will rely on custom MonoDevelop builds being added to PATH.
+                    { EditorId.MonoDevelop, "MonoDevelop.exe" }
+                };
+            }
+            else if (Utils.OS.IsUnix())
             {
             {
-                return "monodevelop";
+                codeEditorPaths = new Dictionary<EditorId, string>
+                {
+                    // Rely on PATH
+                    { EditorId.MonoDevelop, "monodevelop" }
+                };
             }
             }
         }
         }
     }
     }

+ 1 - 0
modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj

@@ -40,6 +40,7 @@
     <Compile Include="Project\ProjectGenerator.cs" />
     <Compile Include="Project\ProjectGenerator.cs" />
     <Compile Include="Project\ProjectUtils.cs" />
     <Compile Include="Project\ProjectUtils.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Utils\OS.cs" />
   </ItemGroup>
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
 </Project>
 </Project>

+ 0 - 14
modules/mono/editor/GodotSharpTools/GodotSharpTools.userprefs

@@ -1,14 +0,0 @@
-<Properties StartupItem="GodotSharpTools.csproj">
-  <MonoDevelop.Ide.Workspace ActiveConfiguration="Debug" />
-  <MonoDevelop.Ide.Workbench ActiveDocument="Build/BuildSystem.cs">
-    <Files>
-      <File FileName="Build/ProjectExtensions.cs" Line="1" Column="1" />
-      <File FileName="Build/ProjectGenerator.cs" Line="1" Column="1" />
-      <File FileName="Build/BuildSystem.cs" Line="37" Column="14" />
-    </Files>
-  </MonoDevelop.Ide.Workbench>
-  <MonoDevelop.Ide.DebuggingService.Breakpoints>
-    <BreakpointStore />
-  </MonoDevelop.Ide.DebuggingService.Breakpoints>
-  <MonoDevelop.Ide.DebuggingService.PinnedWatches />
-</Properties>

+ 62 - 0
modules/mono/editor/GodotSharpTools/Utils/OS.cs

@@ -0,0 +1,62 @@
+using System;
+using System.Linq;
+using System.Runtime.CompilerServices;
+
+namespace GodotSharpTools.Utils
+{
+    public static class OS
+    {
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        extern static string GetPlatformName();
+
+        const string HaikuName = "Haiku";
+        const string OSXName = "OSX";
+        const string ServerName = "Server";
+        const string UWPName = "UWP";
+        const string WindowsName = "Windows";
+        const string X11Name = "X11";
+
+        public static bool IsHaiku()
+        {
+            return HaikuName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
+        }
+
+        public static bool IsOSX()
+        {
+            return OSXName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
+        }
+
+        public static bool IsServer()
+        {
+            return ServerName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
+        }
+
+        public static bool IsUWP()
+        {
+            return UWPName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
+        }
+
+        public static bool IsWindows()
+        {
+            return WindowsName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
+        }
+
+        public static bool IsX11()
+        {
+            return X11Name.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
+        }
+
+        static bool? IsUnixCache = null;
+        static readonly string[] UnixPlatforms = new string[] { HaikuName, OSXName, ServerName, X11Name };
+
+        public static bool IsUnix()
+        {
+            if (IsUnixCache.HasValue)
+                return IsUnixCache.Value;
+
+            string osName = GetPlatformName();
+            IsUnixCache = UnixPlatforms.Any(p => p.Equals(osName, StringComparison.OrdinalIgnoreCase));
+            return IsUnixCache.Value;
+        }
+    }
+}

+ 23 - 4
modules/mono/editor/godotsharp_builds.cpp

@@ -94,7 +94,12 @@ MonoString *godot_icall_BuildInstance_get_MSBuildPath() {
 #if defined(WINDOWS_ENABLED)
 #if defined(WINDOWS_ENABLED)
 	switch (build_tool) {
 	switch (build_tool) {
 		case GodotSharpBuilds::MSBUILD_VS: {
 		case GodotSharpBuilds::MSBUILD_VS: {
-			static String msbuild_tools_path = MonoRegUtils::find_msbuild_tools_path();
+			static String msbuild_tools_path;
+
+			if (msbuild_tools_path.empty() || !FileAccess::exists(msbuild_tools_path)) {
+				// Try to search it again if it wasn't found last time or if it was removed from its location
+				msbuild_tools_path = MonoRegUtils::find_msbuild_tools_path();
+			}
 
 
 			if (msbuild_tools_path.length()) {
 			if (msbuild_tools_path.length()) {
 				if (!msbuild_tools_path.ends_with("\\"))
 				if (!msbuild_tools_path.ends_with("\\"))
@@ -128,15 +133,25 @@ MonoString *godot_icall_BuildInstance_get_MSBuildPath() {
 			CRASH_NOW();
 			CRASH_NOW();
 	}
 	}
 #elif defined(UNIX_ENABLED)
 #elif defined(UNIX_ENABLED)
-	static String msbuild_path = _find_build_engine_on_unix("msbuild");
-	static String xbuild_path = _find_build_engine_on_unix("xbuild");
+	static String msbuild_path;
+	static String xbuild_path;
 
 
 	if (build_tool == GodotSharpBuilds::XBUILD) {
 	if (build_tool == GodotSharpBuilds::XBUILD) {
+		if (xbuild_path.empty() || !FileAccess::exists(xbuild_path)) {
+			// Try to search it again if it wasn't found last time or if it was removed from its location
+			xbuild_path = _find_build_engine_on_unix("msbuild");
+		}
+
 		if (xbuild_path.empty()) {
 		if (xbuild_path.empty()) {
 			WARN_PRINT("Cannot find binary for '" PROP_NAME_XBUILD "'");
 			WARN_PRINT("Cannot find binary for '" PROP_NAME_XBUILD "'");
 			return NULL;
 			return NULL;
 		}
 		}
 	} else {
 	} else {
+		if (msbuild_path.empty() || !FileAccess::exists(msbuild_path)) {
+			// Try to search it again if it wasn't found last time or if it was removed from its location
+			msbuild_path = _find_build_engine_on_unix("msbuild");
+		}
+
 		if (msbuild_path.empty()) {
 		if (msbuild_path.empty()) {
 			WARN_PRINT("Cannot find binary for '" PROP_NAME_MSBUILD_MONO "'");
 			WARN_PRINT("Cannot find binary for '" PROP_NAME_MSBUILD_MONO "'");
 			return NULL;
 			return NULL;
@@ -192,7 +207,11 @@ MonoBoolean godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows() {
 #endif
 #endif
 }
 }
 
 
-void GodotSharpBuilds::_register_internal_calls() {
+void GodotSharpBuilds::register_internal_calls() {
+
+	static bool registered = false;
+	ERR_FAIL_COND(registered);
+	registered = true;
 
 
 	mono_add_internal_call("GodotSharpTools.Build.BuildSystem::godot_icall_BuildInstance_ExitCallback", (void *)godot_icall_BuildInstance_ExitCallback);
 	mono_add_internal_call("GodotSharpTools.Build.BuildSystem::godot_icall_BuildInstance_ExitCallback", (void *)godot_icall_BuildInstance_ExitCallback);
 	mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_MSBuildPath", (void *)godot_icall_BuildInstance_get_MSBuildPath);
 	mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_MSBuildPath", (void *)godot_icall_BuildInstance_get_MSBuildPath);

+ 2 - 3
modules/mono/editor/godotsharp_builds.h

@@ -61,9 +61,6 @@ private:
 
 
 	static GodotSharpBuilds *singleton;
 	static GodotSharpBuilds *singleton;
 
 
-	friend class GDMono;
-	static void _register_internal_calls();
-
 public:
 public:
 	enum BuildTool {
 	enum BuildTool {
 		MSBUILD_MONO,
 		MSBUILD_MONO,
@@ -75,6 +72,8 @@ public:
 
 
 	_FORCE_INLINE_ static GodotSharpBuilds *get_singleton() { return singleton; }
 	_FORCE_INLINE_ static GodotSharpBuilds *get_singleton() { return singleton; }
 
 
+	static void register_internal_calls();
+
 	static void show_build_error_dialog(const String &p_message);
 	static void show_build_error_dialog(const String &p_message);
 
 
 	void build_exit_callback(const MonoBuildInfo &p_build_info, int p_exit_code);
 	void build_exit_callback(const MonoBuildInfo &p_build_info, int p_exit_code);

+ 108 - 12
modules/mono/editor/godotsharp_editor.cpp

@@ -38,12 +38,17 @@
 #include "../csharp_script.h"
 #include "../csharp_script.h"
 #include "../godotsharp_dirs.h"
 #include "../godotsharp_dirs.h"
 #include "../mono_gd/gd_mono.h"
 #include "../mono_gd/gd_mono.h"
+#include "../mono_gd/gd_mono_marshal.h"
 #include "../utils/path_utils.h"
 #include "../utils/path_utils.h"
 #include "bindings_generator.h"
 #include "bindings_generator.h"
 #include "csharp_project.h"
 #include "csharp_project.h"
 #include "godotsharp_export.h"
 #include "godotsharp_export.h"
 #include "net_solution.h"
 #include "net_solution.h"
 
 
+#ifdef OSX_ENABLED
+#include "../utils/osx_utils.h"
+#endif
+
 #ifdef WINDOWS_ENABLED
 #ifdef WINDOWS_ENABLED
 #include "../utils/mono_reg_utils.h"
 #include "../utils/mono_reg_utils.h"
 #endif
 #endif
@@ -169,6 +174,26 @@ void GodotSharpEditor::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("_menu_option_pressed", "id"), &GodotSharpEditor::_menu_option_pressed);
 	ClassDB::bind_method(D_METHOD("_menu_option_pressed", "id"), &GodotSharpEditor::_menu_option_pressed);
 }
 }
 
 
+MonoBoolean godot_icall_MonoDevelopInstance_IsApplicationBundleInstalled(MonoString *p_bundle_id) {
+#ifdef OSX_ENABLED
+	return (MonoBoolean)osx_is_app_bundle_installed(GDMonoMarshal::mono_string_to_godot(p_bundle_id));
+#else
+	(void)p_bundle_id; // UNUSED
+	ERR_FAIL_V(false);
+#endif
+}
+
+void GodotSharpEditor::register_internal_calls() {
+
+	static bool registered = false;
+	ERR_FAIL_COND(registered);
+	registered = true;
+
+	mono_add_internal_call("GodotSharpTools.Editor.MonoDevelopInstance::IsApplicationBundleInstalled", (void *)godot_icall_MonoDevelopInstance_IsApplicationBundleInstalled);
+
+	GodotSharpBuilds::register_internal_calls();
+}
+
 void GodotSharpEditor::show_error_dialog(const String &p_message, const String &p_title) {
 void GodotSharpEditor::show_error_dialog(const String &p_message, const String &p_title) {
 
 
 	error_dialog->set_title(p_title);
 	error_dialog->set_title(p_title);
@@ -181,8 +206,36 @@ Error GodotSharpEditor::open_in_external_editor(const Ref<Script> &p_script, int
 	ExternalEditor editor = ExternalEditor(int(EditorSettings::get_singleton()->get("mono/editor/external_editor")));
 	ExternalEditor editor = ExternalEditor(int(EditorSettings::get_singleton()->get("mono/editor/external_editor")));
 
 
 	switch (editor) {
 	switch (editor) {
-		case EDITOR_CODE: {
+		case EDITOR_VSCODE: {
+			static String vscode_path;
+
+			if (vscode_path.empty() || !FileAccess::exists(vscode_path)) {
+				// Try to search it again if it wasn't found last time or if it was removed from its location
+				vscode_path = path_which("code");
+			}
+
 			List<String> args;
 			List<String> args;
+
+#ifdef OSX_ENABLED
+			// The package path is '/Applications/Visual Studio Code.app'
+			static const String vscode_bundle_id = "com.microsoft.VSCode";
+			static bool osx_app_bundle_installed = osx_is_app_bundle_installed(vscode_bundle_id);
+
+			if (osx_app_bundle_installed) {
+				args.push_back("-b");
+				args.push_back(vscode_bundle_id);
+
+				// The reusing of existing windows made by the 'open' command might not choose a wubdiw that is
+				// editing our folder. It's better to ask for a new window and let VSCode do the window management.
+				args.push_back("-n");
+
+				// The open process must wait until the application finishes (which is instant in VSCode's case)
+				args.push_back("--wait-apps");
+
+				args.push_back("--args");
+			}
+#endif
+
 			args.push_back(ProjectSettings::get_singleton()->get_resource_path());
 			args.push_back(ProjectSettings::get_singleton()->get_resource_path());
 
 
 			String script_path = ProjectSettings::get_singleton()->globalize_path(p_script->get_path());
 			String script_path = ProjectSettings::get_singleton()->globalize_path(p_script->get_path());
@@ -194,18 +247,47 @@ Error GodotSharpEditor::open_in_external_editor(const Ref<Script> &p_script, int
 				args.push_back(script_path);
 				args.push_back(script_path);
 			}
 			}
 
 
-			static String program = path_which("code");
+#ifdef OSX_ENABLED
+			ERR_EXPLAIN("Cannot find code editor: VSCode");
+			ERR_FAIL_COND_V(!osx_app_bundle_installed && vscode_path.empty(), ERR_FILE_NOT_FOUND);
 
 
-			Error err = OS::get_singleton()->execute(program.length() ? program : "code", args, false);
+			String command = osx_app_bundle_installed ? "/usr/bin/open" : vscode_path;
+#else
+			ERR_EXPLAIN("Cannot find code editor: VSCode");
+			ERR_FAIL_COND_V(vscode_path.empty(), ERR_FILE_NOT_FOUND);
+
+			String command = vscode_path;
+#endif
+
+			Error err = OS::get_singleton()->execute(command, args, false);
 
 
 			if (err != OK) {
 			if (err != OK) {
-				ERR_PRINT("GodotSharp: Could not execute external editor");
+				ERR_PRINT("Error when trying to execute code editor: VSCode");
 				return err;
 				return err;
 			}
 			}
 		} break;
 		} break;
+#ifdef OSX_ENABLED
+		case EDITOR_VISUALSTUDIO_MAC:
+			// [[fallthrough]];
+#endif
 		case EDITOR_MONODEVELOP: {
 		case EDITOR_MONODEVELOP: {
-			if (!monodevel_instance)
-				monodevel_instance = memnew(MonoDevelopInstance(GodotSharpDirs::get_project_sln_path()));
+#ifdef OSX_ENABLED
+			bool is_visualstudio = editor == EDITOR_VISUALSTUDIO_MAC;
+
+			MonoDevelopInstance **instance = is_visualstudio ?
+													 &visualstudio_mac_instance :
+													 &monodevelop_instance;
+
+			MonoDevelopInstance::EditorId editor_id = is_visualstudio ?
+															  MonoDevelopInstance::VISUALSTUDIO_FOR_MAC :
+															  MonoDevelopInstance::MONODEVELOP;
+#else
+			MonoDevelopInstance **instance = &monodevelop_instance;
+			MonoDevelopInstance::EditorId editor_id = MonoDevelopInstance::MONODEVELOP;
+#endif
+
+			if (!*instance)
+				*instance = memnew(MonoDevelopInstance(GodotSharpDirs::get_project_sln_path(), editor_id));
 
 
 			String script_path = ProjectSettings::get_singleton()->globalize_path(p_script->get_path());
 			String script_path = ProjectSettings::get_singleton()->globalize_path(p_script->get_path());
 
 
@@ -213,7 +295,7 @@ Error GodotSharpEditor::open_in_external_editor(const Ref<Script> &p_script, int
 				script_path += ";" + itos(p_line + 1) + ";" + itos(p_col);
 				script_path += ";" + itos(p_line + 1) + ";" + itos(p_col);
 			}
 			}
 
 
-			monodevel_instance->execute(script_path);
+			(*instance)->execute(script_path);
 		} break;
 		} break;
 		default:
 		default:
 			return ERR_UNAVAILABLE;
 			return ERR_UNAVAILABLE;
@@ -231,7 +313,10 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) {
 
 
 	singleton = this;
 	singleton = this;
 
 
-	monodevel_instance = NULL;
+	monodevelop_instance = NULL;
+#ifdef OSX_ENABLED
+	visualstudio_mac_instance = NULL;
+#endif
 
 
 	editor = p_editor;
 	editor = p_editor;
 
 
@@ -314,7 +399,18 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) {
 	// External editor settings
 	// External editor settings
 	EditorSettings *ed_settings = EditorSettings::get_singleton();
 	EditorSettings *ed_settings = EditorSettings::get_singleton();
 	EDITOR_DEF("mono/editor/external_editor", EDITOR_NONE);
 	EDITOR_DEF("mono/editor/external_editor", EDITOR_NONE);
-	ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/editor/external_editor", PROPERTY_HINT_ENUM, "None,MonoDevelop,Visual Studio Code"));
+
+	String settings_hint_str = "None";
+
+#ifdef WINDOWS_ENABLED
+	settings_hint_str += ",MonoDevelop,Visual Studio Code";
+#elif OSX_ENABLED
+	settings_hint_str += ",Visual Studio,MonoDevelop,Visual Studio Code";
+#elif UNIX_ENABLED
+	settings_hint_str += ",MonoDevelop,Visual Studio Code";
+#endif
+
+	ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/editor/external_editor", PROPERTY_HINT_ENUM, settings_hint_str));
 
 
 	// Export plugin
 	// Export plugin
 	Ref<GodotSharpExport> godotsharp_export;
 	Ref<GodotSharpExport> godotsharp_export;
@@ -328,9 +424,9 @@ GodotSharpEditor::~GodotSharpEditor() {
 
 
 	memdelete(godotsharp_builds);
 	memdelete(godotsharp_builds);
 
 
-	if (monodevel_instance) {
-		memdelete(monodevel_instance);
-		monodevel_instance = NULL;
+	if (monodevelop_instance) {
+		memdelete(monodevelop_instance);
+		monodevelop_instance = NULL;
 	}
 	}
 }
 }
 
 

+ 17 - 2
modules/mono/editor/godotsharp_editor.h

@@ -50,7 +50,10 @@ class GodotSharpEditor : public Node {
 
 
 	GodotSharpBuilds *godotsharp_builds;
 	GodotSharpBuilds *godotsharp_builds;
 
 
-	MonoDevelopInstance *monodevel_instance;
+	MonoDevelopInstance *monodevelop_instance;
+#ifdef OSX_ENABLED
+	MonoDevelopInstance *visualstudio_mac_instance;
+#endif
 
 
 	bool _create_project_solution();
 	bool _create_project_solution();
 
 
@@ -74,12 +77,24 @@ public:
 
 
 	enum ExternalEditor {
 	enum ExternalEditor {
 		EDITOR_NONE,
 		EDITOR_NONE,
+#ifdef WINDOWS_ENABLED
+		//EDITOR_VISUALSTUDIO, // TODO
 		EDITOR_MONODEVELOP,
 		EDITOR_MONODEVELOP,
-		EDITOR_CODE,
+		EDITOR_VSCODE
+#elif OSX_ENABLED
+		EDITOR_VISUALSTUDIO_MAC,
+		EDITOR_MONODEVELOP,
+		EDITOR_VSCODE
+#elif UNIX_ENABLED
+		EDITOR_MONODEVELOP,
+		EDITOR_VSCODE
+#endif
 	};
 	};
 
 
 	_FORCE_INLINE_ static GodotSharpEditor *get_singleton() { return singleton; }
 	_FORCE_INLINE_ static GodotSharpEditor *get_singleton() { return singleton; }
 
 
+	static void register_internal_calls();
+
 	void show_error_dialog(const String &p_message, const String &p_title = "Error");
 	void show_error_dialog(const String &p_message, const String &p_title = "Error");
 
 
 	Error open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col);
 	Error open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col);

+ 6 - 5
modules/mono/editor/monodevelop_instance.cpp

@@ -47,7 +47,7 @@ void MonoDevelopInstance::execute(const Vector<String> &p_files) {
 	execute_method->invoke(gc_handle->get_target(), args, &exc);
 	execute_method->invoke(gc_handle->get_target(), args, &exc);
 
 
 	if (exc) {
 	if (exc) {
-		GDMonoUtils::debug_unhandled_exception(exc);
+		GDMonoUtils::debug_print_unhandled_exception(exc);
 		ERR_FAIL();
 		ERR_FAIL();
 	}
 	}
 }
 }
@@ -59,7 +59,7 @@ void MonoDevelopInstance::execute(const String &p_file) {
 	execute(files);
 	execute(files);
 }
 }
 
 
-MonoDevelopInstance::MonoDevelopInstance(const String &p_solution) {
+MonoDevelopInstance::MonoDevelopInstance(const String &p_solution, EditorId p_editor_id) {
 
 
 	_GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN)
 	_GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN)
 
 
@@ -67,15 +67,16 @@ MonoDevelopInstance::MonoDevelopInstance(const String &p_solution) {
 
 
 	MonoObject *obj = mono_object_new(TOOLS_DOMAIN, klass->get_mono_ptr());
 	MonoObject *obj = mono_object_new(TOOLS_DOMAIN, klass->get_mono_ptr());
 
 
-	GDMonoMethod *ctor = klass->get_method(".ctor", 1);
+	GDMonoMethod *ctor = klass->get_method(".ctor", 2);
 	MonoException *exc = NULL;
 	MonoException *exc = NULL;
 
 
 	Variant solution = p_solution;
 	Variant solution = p_solution;
-	const Variant *args[1] = { &solution };
+	Variant editor_id = p_editor_id;
+	const Variant *args[2] = { &solution, &editor_id };
 	ctor->invoke(obj, args, &exc);
 	ctor->invoke(obj, args, &exc);
 
 
 	if (exc) {
 	if (exc) {
-		GDMonoUtils::debug_unhandled_exception(exc);
+		GDMonoUtils::debug_print_unhandled_exception(exc);
 		ERR_FAIL();
 		ERR_FAIL();
 	}
 	}
 
 

+ 6 - 1
modules/mono/editor/monodevelop_instance.h

@@ -42,10 +42,15 @@ class MonoDevelopInstance {
 	GDMonoMethod *execute_method;
 	GDMonoMethod *execute_method;
 
 
 public:
 public:
+	enum EditorId {
+		MONODEVELOP = 0,
+		VISUALSTUDIO_FOR_MAC = 1
+	};
+
 	void execute(const Vector<String> &p_files);
 	void execute(const Vector<String> &p_files);
 	void execute(const String &p_file);
 	void execute(const String &p_file);
 
 
-	MonoDevelopInstance(const String &p_solution);
+	MonoDevelopInstance(const String &p_solution, EditorId p_editor_id);
 };
 };
 
 
 #endif // MONODEVELOP_INSTANCE_H
 #endif // MONODEVELOP_INSTANCE_H

+ 1 - 1
modules/mono/mono_gd/gd_mono.cpp

@@ -319,7 +319,7 @@ void GDMono::_register_internal_calls() {
 #endif
 #endif
 
 
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
-	GodotSharpBuilds::_register_internal_calls();
+	GodotSharpEditor::register_internal_calls();
 #endif
 #endif
 }
 }
 
 

+ 62 - 0
modules/mono/utils/osx_utils.cpp

@@ -0,0 +1,62 @@
+/*************************************************************************/
+/*  osx_utils.cpp                                                        */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "osx_utils.h"
+
+#include "core/print_string.h"
+
+#ifdef OSX_ENABLED
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreServices/CoreServices.h>
+
+bool osx_is_app_bundle_installed(const String &p_bundle_id) {
+
+	CFURLRef app_url = NULL;
+	CFStringRef bundle_id = CFStringCreateWithCString(NULL, p_bundle_id.utf8(), kCFStringEncodingUTF8);
+	OSStatus result = LSFindApplicationForInfo(kLSUnknownCreator, bundle_id, NULL, NULL, &app_url);
+	CFRelease(bundle_id);
+
+	if (app_url)
+		CFRelease(app_url);
+
+	switch (result) {
+		case noErr:
+			return true;
+		case kLSApplicationNotFoundErr:
+			break;
+		default:
+			break;
+	}
+
+	return false;
+}
+
+#endif

+ 41 - 0
modules/mono/utils/osx_utils.h

@@ -0,0 +1,41 @@
+/*************************************************************************/
+/*  osx_utils.h                                                          */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "core/ustring.h"
+
+#ifndef OSX_UTILS_H
+
+#ifdef OSX_ENABLED
+
+bool osx_is_app_bundle_installed(const String &p_bundle_id);
+
+#endif
+
+#endif // OSX_UTILS_H