godot/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs

using System;
using System.IO;
using System.Collections.Generic;
using System.Diagnostics;
using GodotTools.Internals;
using GodotTools.Utils;

namespace GodotTools.Ides.MonoDevelop
{
    public class Instance : IDisposable
    {
        public DateTime LaunchTime { get; private set; }
        private readonly string _solutionFile;
        private readonly EditorId _editorId;

        private Process? _process;

        public bool IsRunning => _process != null && !_process.HasExited;
        public bool IsDisposed { get; private set; }

        public void Execute()
        {
            bool newWindow = _process == null || _process.HasExited;

            var args = new List<string>();

            string? command;

            if (OS.IsMacOS)
            {
                string bundleId = BundleIds[_editorId];

                if (Internal.IsMacOSAppBundleInstalled(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 = OS.PathWhich(ExecutableNames[_editorId]);
                }
            }
            else
            {
                command = OS.PathWhich(ExecutableNames[_editorId]);
            }

            args.Add("--ipc-tcp");

            if (newWindow)
                args.Add("\"" + Path.GetFullPath(_solutionFile) + "\"");

            if (command == null)
                throw new FileNotFoundException();

            LaunchTime = DateTime.Now;

            if (newWindow)
            {
                _process = Process.Start(new ProcessStartInfo
                {
                    FileName = command,
                    Arguments = string.Join(" ", args),
                    UseShellExecute = true
                });
            }
            else
            {
                Process.Start(new ProcessStartInfo
                {
                    FileName = command,
                    Arguments = string.Join(" ", args),
                    UseShellExecute = true
                })?.Dispose();
            }
        }

        public Instance(string solutionFile, EditorId editorId)
        {
            if (editorId == EditorId.VisualStudioForMac && !OS.IsMacOS)
                throw new InvalidOperationException($"{nameof(EditorId.VisualStudioForMac)} not supported on this platform");

            _solutionFile = solutionFile;
            _editorId = editorId;
        }

        public void Dispose()
        {
            IsDisposed = true;
            _process?.Dispose();
        }

        private static readonly IReadOnlyDictionary<EditorId, string> ExecutableNames;
        private static readonly IReadOnlyDictionary<EditorId, string> BundleIds;

        static Instance()
        {
            if (OS.IsMacOS)
            {
                ExecutableNames = new Dictionary<EditorId, string>
                {
                    // Rely on PATH
                    {EditorId.MonoDevelop, "monodevelop"},
                    {EditorId.VisualStudioForMac, "VisualStudio"}
                };
                BundleIds = new Dictionary<EditorId, string>
                {
                    // TODO EditorId.MonoDevelop
                    {EditorId.VisualStudioForMac, "com.microsoft.visual-studio"}
                };
            }
            else if (OS.IsWindows)
            {
                ExecutableNames = 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 (OS.IsUnixLike)
            {
                ExecutableNames = new Dictionary<EditorId, string>
                {
                    // Rely on PATH
                    {EditorId.MonoDevelop, "monodevelop"}
                };
            }
            ExecutableNames ??= new Dictionary<EditorId, string>();
            BundleIds ??= new Dictionary<EditorId, string>();
        }
    }
}