diff --git a/Editor/Discovery.cs b/Editor/Discovery.cs index 4144ae0..74a7c9d 100644 --- a/Editor/Discovery.cs +++ b/Editor/Discovery.cs @@ -13,14 +13,6 @@ namespace Microsoft.Unity.VisualStudio.Editor { public static IEnumerable GetVisualStudioInstallations() { -#if UNITY_EDITOR_WIN - foreach (var installation in VisualStudioForWindowsInstallation.GetVisualStudioInstallations()) - yield return installation; -#elif UNITY_EDITOR_OSX - foreach (var installation in VisualStudioForMacInstallation.GetVisualStudioInstallations()) - yield return installation; -#endif - foreach (var installation in VisualStudioCodeInstallation.GetVisualStudioInstallations()) yield return installation; } @@ -29,13 +21,6 @@ namespace Microsoft.Unity.VisualStudio.Editor { try { -#if UNITY_EDITOR_WIN - if (VisualStudioForWindowsInstallation.TryDiscoverInstallation(editorPath, out installation)) - return true; -#elif UNITY_EDITOR_OSX - if (VisualStudioForMacInstallation.TryDiscoverInstallation(editorPath, out installation)) - return true; -#endif if (VisualStudioCodeInstallation.TryDiscoverInstallation(editorPath, out installation)) return true; } @@ -49,11 +34,6 @@ namespace Microsoft.Unity.VisualStudio.Editor public static void Initialize() { -#if UNITY_EDITOR_WIN - VisualStudioForWindowsInstallation.Initialize(); -#elif UNITY_EDITOR_OSX - VisualStudioForMacInstallation.Initialize(); -#endif VisualStudioCodeInstallation.Initialize(); } } diff --git a/Editor/VisualStudioCodeInstallation.cs b/Editor/VisualStudioCodeInstallation.cs index aae57a6..c8ec4c9 100644 --- a/Editor/VisualStudioCodeInstallation.cs +++ b/Editor/VisualStudioCodeInstallation.cs @@ -54,7 +54,8 @@ namespace Microsoft.Unity.VisualStudio.Editor if (string.IsNullOrEmpty(vstuPath)) return Array.Empty(); - return GetAnalyzers(vstuPath); } + return GetAnalyzers(vstuPath); + } public override IGenerator ProjectGenerator { @@ -132,7 +133,7 @@ namespace Microsoft.Unity.VisualStudio.Editor installation = new VisualStudioCodeInstallation() { IsPrerelease = isPrerelease, - Name = "Visual Studio Code" + (isPrerelease ? " - Insider" : string.Empty) + (version != null ? $" [{version.ToString(3)}]" : string.Empty), + Name = "Cursor" + (isPrerelease ? " - Insider" : string.Empty) + (version != null ? $" [{version.ToString(3)}]" : string.Empty), Path = editorPath, Version = version ?? new Version() }; @@ -150,17 +151,16 @@ namespace Microsoft.Unity.VisualStudio.Editor foreach (var basePath in new[] {localAppPath, programFiles}) { - candidates.Add(IOPath.Combine(basePath, "Microsoft VS Code", "Code.exe")); candidates.Add(IOPath.Combine(basePath, "Microsoft VS Code Insiders", "Code - Insiders.exe")); } #elif UNITY_EDITOR_OSX var appPath = IOPath.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles)); - candidates.AddRange(Directory.EnumerateDirectories(appPath, "Visual Studio Code*.app")); + candidates.AddRange(Directory.EnumerateDirectories(appPath, "Cursor*.app")); #elif UNITY_EDITOR_LINUX // Well known locations - candidates.Add("/usr/bin/code"); - candidates.Add("/bin/code"); - candidates.Add("/usr/local/bin/code"); + candidates.Add("/usr/bin/cursor"); + candidates.Add("/bin/cursor"); + candidates.Add("/usr/local/bin/cursor"); // Preference ordered base directories relative to which desktop files should be searched candidates.AddRange(GetXdgCandidates()); @@ -243,7 +243,7 @@ namespace Microsoft.Unity.VisualStudio.Editor } catch (IOException) { - } + } } private const string DefaultLaunchFileContent = @"{ @@ -440,7 +440,7 @@ namespace Microsoft.Unity.VisualStudio.Editor private const string MicrosoftUnityExtensionId = "visualstudiotoolsforunity.vstuc"; private const string DefaultRecommendedExtensionsContent = @"{ ""recommendations"": [ - """+ MicrosoftUnityExtensionId + @""" + """ + MicrosoftUnityExtensionId + @""" ] } "; @@ -506,7 +506,7 @@ namespace Microsoft.Unity.VisualStudio.Editor var directory = IOPath.GetDirectoryName(solution); var application = Path; - ProcessRunner.Start(string.IsNullOrEmpty(path) ? + ProcessRunner.Start(string.IsNullOrEmpty(path) ? ProcessStartInfoFor(application, $"\"{directory}\"") : ProcessStartInfoFor(application, $"\"{directory}\" -g \"{path}\":{line}:{column}")); diff --git a/Editor/VisualStudioForMacInstallation.cs b/Editor/VisualStudioForMacInstallation.cs deleted file mode 100644 index e8f7672..0000000 --- a/Editor/VisualStudioForMacInstallation.cs +++ /dev/null @@ -1,181 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -#if UNITY_EDITOR_OSX - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Runtime.InteropServices; -using System.Text.RegularExpressions; -using Unity.CodeEditor; -using IOPath = System.IO.Path; - -namespace Microsoft.Unity.VisualStudio.Editor -{ - internal class VisualStudioForMacInstallation : VisualStudioInstallation - { - // C# language version support for Visual Studio for Mac - private static readonly VersionPair[] OSXVersionTable = - { - // VisualStudio for Mac 2022 - new VersionPair(17,4, /* => */ 11,0), - new VersionPair(17,0, /* => */ 10,0), - - // VisualStudio for Mac 8.x - new VersionPair(8,8, /* => */ 9,0), - new VersionPair(8,3, /* => */ 8,0), - new VersionPair(8,0, /* => */ 7,3), - }; - - private static readonly IGenerator _generator = new LegacyStyleProjectGeneration(); - - public override bool SupportsAnalyzers - { - get - { - return Version >= new Version(8, 3); - } - } - - public override Version LatestLanguageVersionSupported - { - get - { - return GetLatestLanguageVersionSupported(OSXVersionTable); - } - } - - private string GetExtensionPath() - { - const string addinName = "MonoDevelop.Unity"; - const string addinAssembly = addinName + ".dll"; - - // user addins repository - var localAddins = IOPath.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.Personal), - $"Library/Application Support/VisualStudio/${Version.Major}.0" + "/LocalInstall/Addins"); - - // In the user addins repository, the addins are suffixed by their versions, like `MonoDevelop.Unity.1.0` - // When installing another local user addin, MD will remove files inside the folder - // So we browse all VSTUM addins, and return the one with an addin assembly - if (Directory.Exists(localAddins)) - { - foreach (var folder in Directory.GetDirectories(localAddins, addinName + "*", SearchOption.TopDirectoryOnly)) - { - if (File.Exists(IOPath.Combine(folder, addinAssembly))) - return folder; - } - } - - // Check in Visual Studio.app/ - // In that case the name of the addin is used - var addinPath = IOPath.Combine(Path, $"Contents/Resources/lib/monodevelop/AddIns/{addinName}"); - if (File.Exists(IOPath.Combine(addinPath, addinAssembly))) - return addinPath; - - addinPath = IOPath.Combine(Path, $"Contents/MonoBundle/Addins/{addinName}"); - if (File.Exists(IOPath.Combine(addinPath, addinAssembly))) - return addinPath; - - return null; - } - - public override string[] GetAnalyzers() - { - var vstuPath = GetExtensionPath(); - if (string.IsNullOrEmpty(vstuPath)) - return Array.Empty(); - - return GetAnalyzers(vstuPath); - } - - public override IGenerator ProjectGenerator - { - get - { - return _generator; - } - } - - private static bool IsCandidateForDiscovery(string path) - { - return Directory.Exists(path) && Regex.IsMatch(path, "Visual\\s?Studio(?!.*Code.*).*.app$", RegexOptions.IgnoreCase); - } - - public static bool TryDiscoverInstallation(string editorPath, out IVisualStudioInstallation installation) - { - installation = null; - - if (string.IsNullOrEmpty(editorPath)) - return false; - - if (!IsCandidateForDiscovery(editorPath)) - return false; - - // On Mac we use the .app folder, so we need to access to main assembly - var fvi = IOPath.Combine(editorPath, "Contents/Resources/lib/monodevelop/bin/VisualStudio.exe"); - - if (!File.Exists(fvi)) - fvi = IOPath.Combine(editorPath, "Contents/MonoBundle/VisualStudio.exe"); - - if (!File.Exists(fvi)) - fvi = IOPath.Combine(editorPath, "Contents/MonoBundle/VisualStudio.dll"); - - if (!File.Exists(fvi)) - return false; - - // VS preview are not using the isPrerelease flag so far - // On Windows FileDescription contains "Preview", but not on Mac - var vi = FileVersionInfo.GetVersionInfo(fvi); - var version = new Version(vi.ProductVersion); - var isPrerelease = vi.IsPreRelease || string.Concat(editorPath, "/" + vi.FileDescription).ToLower().Contains("preview"); - - installation = new VisualStudioForMacInstallation() - { - IsPrerelease = isPrerelease, - Name = $"{vi.FileDescription}{(isPrerelease ? " Preview" : string.Empty)} [{version.ToString(3)}]", - Path = editorPath, - Version = version - }; - return true; - } - - public static IEnumerable GetVisualStudioInstallations() - { - var candidates = Directory.EnumerateDirectories("/Applications", "*.app"); - foreach (var candidate in candidates) - { - if (TryDiscoverInstallation(candidate, out var installation)) - yield return installation; - } - } - - [DllImport("AppleEventIntegration")] - private static extern bool OpenVisualStudio(string appPath, string solutionPath, string filePath, int line); - - public override void CreateExtraFiles(string projectDirectory) - { - } - - public override bool Open(string path, int line, int column, string solution) - { - string absolutePath = ""; - if (!string.IsNullOrWhiteSpace(path)) - { - absolutePath = IOPath.GetFullPath(path); - } - - return OpenVisualStudio(CodeEditor.CurrentEditorInstallation, solution, absolutePath, line); - } - - public static void Initialize() - { - } - } -} - -#endif diff --git a/Editor/VisualStudioForMacInstallation.cs.meta b/Editor/VisualStudioForMacInstallation.cs.meta deleted file mode 100644 index dc0c105..0000000 --- a/Editor/VisualStudioForMacInstallation.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 2c64241ee5e302b478d7f2522bbaa4e3 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Editor/VisualStudioForWindowsInstallation.cs b/Editor/VisualStudioForWindowsInstallation.cs deleted file mode 100644 index 862ecd8..0000000 --- a/Editor/VisualStudioForWindowsInstallation.cs +++ /dev/null @@ -1,380 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -#if UNITY_EDITOR_WIN - -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text.RegularExpressions; -using Microsoft.Win32; -using Unity.CodeEditor; -using UnityEditor; -using UnityEngine; -using Debug = UnityEngine.Debug; -using IOPath = System.IO.Path; - -namespace Microsoft.Unity.VisualStudio.Editor -{ - internal class VisualStudioForWindowsInstallation : VisualStudioInstallation - { - // C# language version support for Visual Studio - private static readonly VersionPair[] WindowsVersionTable = - { - // VisualStudio 2022 - new VersionPair(17,4, /* => */ 11,0), - new VersionPair(17,0, /* => */ 10,0), - - // VisualStudio 2019 - new VersionPair(16,8, /* => */ 9,0), - new VersionPair(16,0, /* => */ 8,0), - - // VisualStudio 2017 - new VersionPair(15,7, /* => */ 7,3), - new VersionPair(15,5, /* => */ 7,2), - new VersionPair(15,3, /* => */ 7,1), - new VersionPair(15,0, /* => */ 7,0), - }; - - private static string _vsWherePath = null; - private static readonly IGenerator _generator = new LegacyStyleProjectGeneration(); - - public override bool SupportsAnalyzers - { - get - { - return Version >= new Version(16, 3); - } - } - - public override Version LatestLanguageVersionSupported - { - get - { - return GetLatestLanguageVersionSupported(WindowsVersionTable); - } - } - - private static string ReadRegistry(RegistryKey hive, string keyName, string valueName) - { - try - { - var unitykey = hive.OpenSubKey(keyName); - - var result = (string)unitykey?.GetValue(valueName); - return result; - } - catch (Exception) - { - return null; - } - } - - private string GetWindowsBridgeFromRegistry() - { - var keyName = $"Software\\Microsoft\\Microsoft Visual Studio {Version.Major}.0 Tools for Unity"; - const string valueName = "UnityExtensionPath"; - - var bridge = ReadRegistry(Registry.CurrentUser, keyName, valueName); - if (string.IsNullOrEmpty(bridge)) - bridge = ReadRegistry(Registry.LocalMachine, keyName, valueName); - - return bridge; - } - - private string GetExtensionPath() - { - const string extensionName = "Visual Studio Tools for Unity"; - const string extensionAssembly = "SyntaxTree.VisualStudio.Unity.dll"; - - var vsDirectory = IOPath.GetDirectoryName(Path); - var vstuDirectory = IOPath.Combine(vsDirectory, "Extensions", "Microsoft", extensionName); - - if (File.Exists(IOPath.Combine(vstuDirectory, extensionAssembly))) - return vstuDirectory; - - return null; - } - - public override string[] GetAnalyzers() - { - var vstuPath = GetExtensionPath(); - if (string.IsNullOrEmpty(vstuPath)) - return Array.Empty(); - - var analyzers = GetAnalyzers(vstuPath); - if (analyzers?.Length > 0) - return analyzers; - - var bridge = GetWindowsBridgeFromRegistry(); - if (File.Exists(bridge)) - return GetAnalyzers(IOPath.Combine(IOPath.GetDirectoryName(bridge), "..")); - - return Array.Empty(); - } - - public override IGenerator ProjectGenerator - { - get - { - return _generator; - } - } - - private static bool IsCandidateForDiscovery(string path) - { - return File.Exists(path) && Regex.IsMatch(path, "devenv.exe$", RegexOptions.IgnoreCase); - } - - public static bool TryDiscoverInstallation(string editorPath, out IVisualStudioInstallation installation) - { - installation = null; - - if (string.IsNullOrEmpty(editorPath)) - return false; - - if (!IsCandidateForDiscovery(editorPath)) - return false; - - // On windows we use the executable directly, so we can query extra information - if (!File.Exists(editorPath)) - return false; - - // VS preview are not using the isPrerelease flag so far - // On Windows FileDescription contains "Preview", but not on Mac - var vi = FileVersionInfo.GetVersionInfo(editorPath); - var version = new Version(vi.ProductVersion); - var isPrerelease = vi.IsPreRelease || string.Concat(editorPath, "/" + vi.FileDescription).ToLower().Contains("preview"); - - installation = new VisualStudioForWindowsInstallation() - { - IsPrerelease = isPrerelease, - Name = $"{FormatProductName(vi.FileDescription)} [{version.ToString(3)}]", - Path = editorPath, - Version = version - }; - return true; - } - - public static string FormatProductName(string productName) - { - if (string.IsNullOrEmpty(productName)) - return string.Empty; - - return productName.Replace("Microsoft ", string.Empty); - } - - public static IEnumerable GetVisualStudioInstallations() - { - foreach (var installation in QueryVsWhere()) - yield return installation; - } - - #region VsWhere Json Schema -#pragma warning disable CS0649 - [Serializable] - internal class VsWhereResult - { - public VsWhereEntry[] entries; - - public static VsWhereResult FromJson(string json) - { - return JsonUtility.FromJson("{ \"" + nameof(VsWhereResult.entries) + "\": " + json + " }"); - } - - public IEnumerable ToVisualStudioInstallations() - { - foreach (var entry in entries) - { - yield return new VisualStudioForWindowsInstallation - { - Name = $"{FormatProductName(entry.displayName)} [{entry.catalog.productDisplayVersion}]", - Path = entry.productPath, - IsPrerelease = entry.isPrerelease, - Version = Version.Parse(entry.catalog.buildVersion) - }; - } - } - } - - [Serializable] - internal class VsWhereEntry - { - public string displayName; - public bool isPrerelease; - public string productPath; - public VsWhereCatalog catalog; - } - - [Serializable] - internal class VsWhereCatalog - { - public string productDisplayVersion; // non parseable like "16.3.0 Preview 3.0" - public string buildVersion; - } -#pragma warning restore CS3021 - #endregion - - private static IEnumerable QueryVsWhere() - { - var progpath = _vsWherePath; - - if (string.IsNullOrWhiteSpace(progpath)) - return Enumerable.Empty(); - - const string arguments = "-prerelease -format json"; - - // We've seen issues with json parsing in utf8 mode and with specific non-UTF code pages like 949 (Korea) - // So try with utf8 first, then fallback to non utf8 in case of an issue - // See https://github.com/microsoft/vswhere/issues/264 - try - { - return QueryVsWhere(progpath, $"{arguments} -utf8"); - } - catch - { - return QueryVsWhere(progpath, $"{arguments}"); - } - } - - private static IEnumerable QueryVsWhere(string progpath, string arguments) - { - var result = ProcessRunner.StartAndWaitForExit(progpath, arguments); - - if (!result.Success) - throw new Exception($"Failure while running vswhere: {result.Error}"); - - // Do not catch any JsonException here, this will be handled by the caller - return VsWhereResult - .FromJson(result.Output) - .ToVisualStudioInstallations(); - } - - private enum COMIntegrationState - { - Running, - DisplayProgressBar, - ClearProgressBar, - Exited - } - - public override void CreateExtraFiles(string projectDirectory) - { - // See https://devblogs.microsoft.com/setup/configure-visual-studio-across-your-organization-with-vsconfig/ - // We create a .vsconfig file to make sure our ManagedGame workload is installed - try - { - var vsConfigFile = IOPath.Combine(projectDirectory.NormalizePathSeparators(), ".vsconfig"); - if (File.Exists(vsConfigFile)) - return; - - const string content = @"{ - ""version"": ""1.0"", - ""components"": [ - ""Microsoft.VisualStudio.Workload.ManagedGame"" - ] -} -"; - File.WriteAllText(vsConfigFile, content); - } - catch (IOException) - { - } - } - - public override bool Open(string path, int line, int column, string solution) - { - var progpath = FileUtility.GetPackageAssetFullPath("Editor", "COMIntegration", "Release", "COMIntegration.exe"); - - if (string.IsNullOrWhiteSpace(progpath)) - return false; - - string absolutePath = ""; - if (!string.IsNullOrWhiteSpace(path)) - { - absolutePath = IOPath.GetFullPath(path); - } - - // We remove all invalid chars from the solution filename, but we cannot prevent the user from using a specific path for the Unity project - // So process the fullpath to make it compatible with VS - if (!string.IsNullOrWhiteSpace(solution)) - { - solution = $"\"{solution}\""; - solution = solution.Replace("^", "^^"); - } - - var psi = ProcessRunner.ProcessStartInfoFor(progpath, $"\"{CodeEditor.CurrentEditorInstallation}\" {solution} \"{absolutePath}\" {line}"); - psi.StandardOutputEncoding = System.Text.Encoding.Unicode; - psi.StandardErrorEncoding = System.Text.Encoding.Unicode; - - // inter thread communication - var messages = new BlockingCollection(); - - var asyncStart = AsyncOperation.Run( - () => ProcessRunner.StartAndWaitForExit(psi, onOutputReceived: data => OnOutputReceived(data, messages)), - e => new ProcessRunnerResult {Success = false, Error = e.Message, Output = string.Empty}, - () => messages.Add(COMIntegrationState.Exited) - ); - - MonitorCOMIntegration(messages); - - var result = asyncStart.Result; - - if (!result.Success && !string.IsNullOrWhiteSpace(result.Error)) - Debug.LogError($"Error while starting Visual Studio: {result.Error}"); - - return result.Success; - } - - private static void MonitorCOMIntegration(BlockingCollection messages) - { - var displayingProgress = false; - COMIntegrationState state; - - do - { - state = messages.Take(); - switch (state) - { - case COMIntegrationState.ClearProgressBar: - EditorUtility.ClearProgressBar(); - displayingProgress = false; - break; - case COMIntegrationState.DisplayProgressBar: - EditorUtility.DisplayProgressBar("Opening Visual Studio", "Starting up Visual Studio, this might take some time.", .5f); - displayingProgress = true; - break; - } - } while (state != COMIntegrationState.Exited); - - // Make sure the progress bar is properly cleared in case of COMIntegration failure - if (displayingProgress) - EditorUtility.ClearProgressBar(); - } - - private static readonly COMIntegrationState[] ProgressBarCommands = {COMIntegrationState.DisplayProgressBar, COMIntegrationState.ClearProgressBar}; - private static void OnOutputReceived(string data, BlockingCollection messages) - { - if (data == null) - return; - - foreach (var cmd in ProgressBarCommands) - { - if (data.IndexOf(cmd.ToString(), StringComparison.OrdinalIgnoreCase) >= 0) - messages.Add(cmd); - } - } - - public static void Initialize() - { - _vsWherePath = FileUtility.GetPackageAssetFullPath("Editor", "VSWhere", "vswhere.exe"); - } - } -} - -#endif diff --git a/Editor/VisualStudioForWindowsInstallation.cs.meta b/Editor/VisualStudioForWindowsInstallation.cs.meta deleted file mode 100644 index ca91ae2..0000000 --- a/Editor/VisualStudioForWindowsInstallation.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: be7ef402265a7a549b2e43c11d1a22c5 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/package.json b/package.json index 78ebf58..53a49bc 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "com.unity.ide.visualstudio", - "displayName": "Visual Studio Editor", - "description": "Code editor integration for supporting Visual Studio as code editor for unity. Adds support for generating csproj files for intellisense purposes, auto discovery of installations, etc.", + "name": "com.unity.ide.cursor", + "displayName": "Cursor Editor", + "description": "Code editor integration for supporting Cursor as code editor for unity. Adds support for generating csproj files for intellisense purposes, auto discovery of installations, etc.", "version": "2.0.22", "unity": "2019.4", "unityRelease": "25f1", @@ -23,4 +23,4 @@ "type": "git", "revision": "700b44077345e97d37d464ff25507638983aed64" } -} +} \ No newline at end of file