A game about forced loneliness, made by TACStudios
1/*---------------------------------------------------------------------------------------------
2 * Copyright (c) Microsoft Corporation. All rights reserved.
3 * Licensed under the MIT License. See License.txt in the project root for license information.
4 *--------------------------------------------------------------------------------------------*/
5
6#if UNITY_EDITOR_OSX
7
8using System;
9using System.Collections.Generic;
10using System.Diagnostics;
11using System.IO;
12using System.Runtime.InteropServices;
13using System.Text.RegularExpressions;
14using Unity.CodeEditor;
15using IOPath = System.IO.Path;
16
17namespace Microsoft.Unity.VisualStudio.Editor
18{
19 internal class VisualStudioForMacInstallation : VisualStudioInstallation
20 {
21 // C# language version support for Visual Studio for Mac
22 private static readonly VersionPair[] OSXVersionTable =
23 {
24 // VisualStudio for Mac 2022
25 new VersionPair(17,4, /* => */ 11,0),
26 new VersionPair(17,0, /* => */ 10,0),
27
28 // VisualStudio for Mac 8.x
29 new VersionPair(8,8, /* => */ 9,0),
30 new VersionPair(8,3, /* => */ 8,0),
31 new VersionPair(8,0, /* => */ 7,3),
32 };
33
34 private static readonly IGenerator _generator = new LegacyStyleProjectGeneration();
35
36 public override bool SupportsAnalyzers
37 {
38 get
39 {
40 return Version >= new Version(8, 3);
41 }
42 }
43
44 public override Version LatestLanguageVersionSupported
45 {
46 get
47 {
48 return GetLatestLanguageVersionSupported(OSXVersionTable);
49 }
50 }
51
52 private string GetExtensionPath()
53 {
54 const string addinName = "MonoDevelop.Unity";
55 const string addinAssembly = addinName + ".dll";
56
57 // user addins repository
58 var localAddins = IOPath.Combine(
59 Environment.GetFolderPath(Environment.SpecialFolder.Personal),
60 $"Library/Application Support/VisualStudio/${Version.Major}.0" + "/LocalInstall/Addins");
61
62 // In the user addins repository, the addins are suffixed by their versions, like `MonoDevelop.Unity.1.0`
63 // When installing another local user addin, MD will remove files inside the folder
64 // So we browse all VSTUM addins, and return the one with an addin assembly
65 if (Directory.Exists(localAddins))
66 {
67 foreach (var folder in Directory.GetDirectories(localAddins, addinName + "*", SearchOption.TopDirectoryOnly))
68 {
69 if (File.Exists(IOPath.Combine(folder, addinAssembly)))
70 return folder;
71 }
72 }
73
74 // Check in Visual Studio.app/
75 // In that case the name of the addin is used
76 var addinPath = IOPath.Combine(Path, $"Contents/Resources/lib/monodevelop/AddIns/{addinName}");
77 if (File.Exists(IOPath.Combine(addinPath, addinAssembly)))
78 return addinPath;
79
80 addinPath = IOPath.Combine(Path, $"Contents/MonoBundle/Addins/{addinName}");
81 if (File.Exists(IOPath.Combine(addinPath, addinAssembly)))
82 return addinPath;
83
84 return null;
85 }
86
87 public override string[] GetAnalyzers()
88 {
89 var vstuPath = GetExtensionPath();
90 if (string.IsNullOrEmpty(vstuPath))
91 return Array.Empty<string>();
92
93 return GetAnalyzers(vstuPath);
94 }
95
96 public override IGenerator ProjectGenerator
97 {
98 get
99 {
100 return _generator;
101 }
102 }
103
104 private static bool IsCandidateForDiscovery(string path)
105 {
106 return Directory.Exists(path) && Regex.IsMatch(path, "Visual\\s?Studio(?!.*Code.*).*.app$", RegexOptions.IgnoreCase);
107 }
108
109 public static bool TryDiscoverInstallation(string editorPath, out IVisualStudioInstallation installation)
110 {
111 installation = null;
112
113 if (string.IsNullOrEmpty(editorPath))
114 return false;
115
116 if (!IsCandidateForDiscovery(editorPath))
117 return false;
118
119 // On Mac we use the .app folder, so we need to access to main assembly
120 var fvi = IOPath.Combine(editorPath, "Contents/Resources/lib/monodevelop/bin/VisualStudio.exe");
121
122 if (!File.Exists(fvi))
123 fvi = IOPath.Combine(editorPath, "Contents/MonoBundle/VisualStudio.exe");
124
125 if (!File.Exists(fvi))
126 fvi = IOPath.Combine(editorPath, "Contents/MonoBundle/VisualStudio.dll");
127
128 if (!File.Exists(fvi))
129 return false;
130
131 // VS preview are not using the isPrerelease flag so far
132 // On Windows FileDescription contains "Preview", but not on Mac
133 var vi = FileVersionInfo.GetVersionInfo(fvi);
134 var version = new Version(vi.ProductVersion);
135 var isPrerelease = vi.IsPreRelease || string.Concat(editorPath, "/" + vi.FileDescription).ToLower().Contains("preview");
136
137 installation = new VisualStudioForMacInstallation()
138 {
139 IsPrerelease = isPrerelease,
140 Name = $"{vi.FileDescription}{(isPrerelease ? " Preview" : string.Empty)} [{version.ToString(3)}]",
141 Path = editorPath,
142 Version = version
143 };
144 return true;
145 }
146
147 public static IEnumerable<IVisualStudioInstallation> GetVisualStudioInstallations()
148 {
149 var candidates = Directory.EnumerateDirectories("/Applications", "*.app");
150 foreach (var candidate in candidates)
151 {
152 if (TryDiscoverInstallation(candidate, out var installation))
153 yield return installation;
154 }
155 }
156
157 [DllImport("AppleEventIntegration")]
158 private static extern bool OpenVisualStudio(string appPath, string solutionPath, string filePath, int line);
159
160 public override void CreateExtraFiles(string projectDirectory)
161 {
162 }
163
164 public override bool Open(string path, int line, int column, string solution)
165 {
166 string absolutePath = "";
167 if (!string.IsNullOrWhiteSpace(path))
168 {
169 absolutePath = IOPath.GetFullPath(path);
170 }
171
172 return OpenVisualStudio(CodeEditor.CurrentEditorInstallation, solution, absolutePath, line);
173 }
174
175 public static void Initialize()
176 {
177 }
178 }
179}
180
181#endif