8 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
8e1da25983 Add clarifying comment for Unity 2019.4 C# 7.3 compatibility
Co-authored-by: OmarAlFarajat <OmarAlFarajat@users.noreply.github.com>

Co-authored-by: boxqkrtm <8157743+boxqkrtm@users.noreply.github.com>
2026-02-12 10:03:40 +00:00
copilot-swe-agent[bot]
412c992de2 Fix CS1525 compiler error for Unity 2019.4 by replacing ??= with version-compatible code
Co-authored-by: boxqkrtm <8157743+boxqkrtm@users.noreply.github.com>
2026-02-12 09:57:19 +00:00
copilot-swe-agent[bot]
d8164f72bb Initial plan 2026-02-12 09:55:31 +00:00
goethe
a97262d3f7 performance optimize for opening file (#36) 2026-02-12 18:34:39 +09:00
boxqkrtm
53e1baac3c feat(2.0.27): Add Multiple or Single Cursor Instance Options (#31)
* feat: Add Multiple or Single Cursor Instance Options

* chore: remove too verbose log
2025-11-02 14:35:27 +09:00
boxqkrtm
8c1a4ac3e0 docs: update package.json
- Update version to 2.0.26
- Update changelog for workspace support
2025-11-02 13:06:17 +09:00
yahui.han
84578b3b24 code-workspace 2025-11-02 13:06:17 +09:00
boxqkrtm
38fecf55e4 Update README.md 2025-05-19 19:04:34 +09:00
6 changed files with 218 additions and 91 deletions

View File

@@ -1,4 +1,16 @@
# Code Editor Package for Visual Studio # Code Editor Package for Cursor
## [2.0.27] - 2025-11-02
Integration:
- Add Multiple or Single Cursor Instance Options
## [2.0.26] - 2025-11-02
Integration:
- Add workspace support
## [2.0.22] - 2023-10-03 ## [2.0.22] - 2023-10-03
@@ -48,7 +60,7 @@ Project generation:
Integration: Integration:
- Performance improvements with `EditorApplication.update` callbacks. - Performance improvements with `EditorApplication.update` callbacks.
Project generation: Project generation:
- Add extra compiler options for analyzers and source generators. - Add extra compiler options for analyzers and source generators.
@@ -71,7 +83,7 @@ Project generation:
Integration: Integration:
- Prevent ADB Refresh while being in safe-mode with a URP project - Prevent ADB Refresh while being in safe-mode with a URP project
- Fixed an issue keeping the progress bar visible even after opening a script with Visual Studio. - Fixed an issue keeping the progress bar visible even after opening a script with Visual Studio.
## [2.0.15] - 2022-03-21 ## [2.0.15] - 2022-03-21

View File

@@ -135,8 +135,6 @@ namespace Microsoft.Unity.VisualStudio.Editor
cursorStoragePath = Path.Combine(userProfile, "AppData", "Roaming", "cursor", "User", "workspaceStorage"); cursorStoragePath = Path.Combine(userProfile, "AppData", "Roaming", "cursor", "User", "workspaceStorage");
#endif #endif
Debug.Log($"[Cursor] Looking for workspaces in: {cursorStoragePath}");
if (Directory.Exists(cursorStoragePath)) if (Directory.Exists(cursorStoragePath))
{ {
foreach (var workspaceDir in Directory.GetDirectories(cursorStoragePath)) foreach (var workspaceDir in Directory.GetDirectories(cursorStoragePath))

View File

@@ -10,27 +10,36 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using UnityEngine; using UnityEngine;
using UnityEditor;
using SimpleJSON; using SimpleJSON;
using IOPath = System.IO.Path; using IOPath = System.IO.Path;
using Debug = UnityEngine.Debug; using Debug = UnityEngine.Debug;
namespace Microsoft.Unity.VisualStudio.Editor { namespace Microsoft.Unity.VisualStudio.Editor
internal class VisualStudioCursorInstallation : VisualStudioInstallation { {
internal class VisualStudioCursorInstallation : VisualStudioInstallation
{
private static readonly IGenerator _generator = new SdkStyleProjectGeneration(); private static readonly IGenerator _generator = new SdkStyleProjectGeneration();
internal const string ReuseExistingWindowKey = "cursor_reuse_existing_window";
public override bool SupportsAnalyzers { public override bool SupportsAnalyzers
get { {
get
{
return true; return true;
} }
} }
public override Version LatestLanguageVersionSupported { public override Version LatestLanguageVersionSupported
get { {
get
{
return new Version(11, 0); return new Version(11, 0);
} }
} }
private string GetExtensionPath() { private string GetExtensionPath()
{
var vscode = IsPrerelease ? ".vscode-insiders" : ".vscode"; var vscode = IsPrerelease ? ".vscode-insiders" : ".vscode";
var extensionsPath = IOPath.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), vscode, "extensions"); var extensionsPath = IOPath.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), vscode, "extensions");
if (!Directory.Exists(extensionsPath)) if (!Directory.Exists(extensionsPath))
@@ -42,7 +51,8 @@ namespace Microsoft.Unity.VisualStudio.Editor {
.FirstOrDefault(); .FirstOrDefault();
} }
public override string[] GetAnalyzers() { public override string[] GetAnalyzers()
{
var vstuPath = GetExtensionPath(); var vstuPath = GetExtensionPath();
if (string.IsNullOrEmpty(vstuPath)) if (string.IsNullOrEmpty(vstuPath))
return Array.Empty<string>(); return Array.Empty<string>();
@@ -50,13 +60,16 @@ namespace Microsoft.Unity.VisualStudio.Editor {
return GetAnalyzers(vstuPath); return GetAnalyzers(vstuPath);
} }
public override IGenerator ProjectGenerator { public override IGenerator ProjectGenerator
get { {
get
{
return _generator; return _generator;
} }
} }
private static bool IsCandidateForDiscovery(string path) { private static bool IsCandidateForDiscovery(string path)
{
#if UNITY_EDITOR_OSX #if UNITY_EDITOR_OSX
return Directory.Exists(path) && Regex.IsMatch(path, ".*Cursor.*.app$", RegexOptions.IgnoreCase); return Directory.Exists(path) && Regex.IsMatch(path, ".*Cursor.*.app$", RegexOptions.IgnoreCase);
#elif UNITY_EDITOR_WIN #elif UNITY_EDITOR_WIN
@@ -67,12 +80,14 @@ namespace Microsoft.Unity.VisualStudio.Editor {
} }
[Serializable] [Serializable]
internal class VisualStudioCodeManifest { internal class VisualStudioCodeManifest
{
public string name; public string name;
public string version; public string version;
} }
public static bool TryDiscoverInstallation(string editorPath, out IVisualStudioInstallation installation) { public static bool TryDiscoverInstallation(string editorPath, out IVisualStudioInstallation installation)
{
installation = null; installation = null;
if (string.IsNullOrEmpty(editorPath)) if (string.IsNullOrEmpty(editorPath))
@@ -84,7 +99,8 @@ namespace Microsoft.Unity.VisualStudio.Editor {
Version version = null; Version version = null;
var isPrerelease = false; var isPrerelease = false;
try { try
{
var manifestBase = GetRealPath(editorPath); var manifestBase = GetRealPath(editorPath);
#if UNITY_EDITOR_WIN #if UNITY_EDITOR_WIN
@@ -104,17 +120,21 @@ namespace Microsoft.Unity.VisualStudio.Editor {
return false; return false;
var manifestFullPath = IOPath.Combine(manifestBase, "resources", "app", "package.json"); var manifestFullPath = IOPath.Combine(manifestBase, "resources", "app", "package.json");
if (File.Exists(manifestFullPath)) { if (File.Exists(manifestFullPath))
{
var manifest = JsonUtility.FromJson<VisualStudioCodeManifest>(File.ReadAllText(manifestFullPath)); var manifest = JsonUtility.FromJson<VisualStudioCodeManifest>(File.ReadAllText(manifestFullPath));
Version.TryParse(manifest.version.Split('-').First(), out version); Version.TryParse(manifest.version.Split('-').First(), out version);
isPrerelease = manifest.version.ToLower().Contains("insider"); isPrerelease = manifest.version.ToLower().Contains("insider");
} }
} catch (Exception) { }
catch (Exception)
{
// do not fail if we are not able to retrieve the exact version number // do not fail if we are not able to retrieve the exact version number
} }
isPrerelease = isPrerelease || editorPath.ToLower().Contains("insider"); isPrerelease = isPrerelease || editorPath.ToLower().Contains("insider");
installation = new VisualStudioCursorInstallation() { installation = new VisualStudioCursorInstallation()
{
IsPrerelease = isPrerelease, IsPrerelease = isPrerelease,
Name = "Cursor" + (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, Path = editorPath,
@@ -124,7 +144,8 @@ namespace Microsoft.Unity.VisualStudio.Editor {
return true; return true;
} }
public static IEnumerable<IVisualStudioInstallation> GetVisualStudioInstallations() { public static IEnumerable<IVisualStudioInstallation> GetVisualStudioInstallations()
{
var candidates = new List<string>(); var candidates = new List<string>();
#if UNITY_EDITOR_WIN #if UNITY_EDITOR_WIN
@@ -147,7 +168,8 @@ namespace Microsoft.Unity.VisualStudio.Editor {
candidates.AddRange(GetXdgCandidates()); candidates.AddRange(GetXdgCandidates());
#endif #endif
foreach (var candidate in candidates.Distinct()) { foreach (var candidate in candidates.Distinct())
{
if (TryDiscoverInstallation(candidate, out var installation)) if (TryDiscoverInstallation(candidate, out var installation))
yield return installation; yield return installation;
} }
@@ -172,7 +194,7 @@ namespace Microsoft.Unity.VisualStudio.Editor {
var desktopFile = IOPath.Combine(dir, "applications/code.desktop"); var desktopFile = IOPath.Combine(dir, "applications/code.desktop");
if (!File.Exists(desktopFile)) if (!File.Exists(desktopFile))
continue; continue;
var content = File.ReadAllText(desktopFile); var content = File.ReadAllText(desktopFile);
match = DesktopFileExecEntry.Match(content); match = DesktopFileExecEntry.Match(content);
} }
@@ -202,13 +224,16 @@ namespace Microsoft.Unity.VisualStudio.Editor {
return new String(cbuf, 0, chars); return new String(cbuf, 0, chars);
} }
#else #else
internal static string GetRealPath(string path) { internal static string GetRealPath(string path)
{
return path; return path;
} }
#endif #endif
public override void CreateExtraFiles(string projectDirectory) { public override void CreateExtraFiles(string projectDirectory)
try { {
try
{
var vscodeDirectory = IOPath.Combine(projectDirectory.NormalizePathSeparators(), ".vscode"); var vscodeDirectory = IOPath.Combine(projectDirectory.NormalizePathSeparators(), ".vscode");
Directory.CreateDirectory(vscodeDirectory); Directory.CreateDirectory(vscodeDirectory);
@@ -217,7 +242,9 @@ namespace Microsoft.Unity.VisualStudio.Editor {
CreateRecommendedExtensionsFile(vscodeDirectory, enablePatch); CreateRecommendedExtensionsFile(vscodeDirectory, enablePatch);
CreateSettingsFile(vscodeDirectory, enablePatch); CreateSettingsFile(vscodeDirectory, enablePatch);
CreateLaunchFile(vscodeDirectory, enablePatch); CreateLaunchFile(vscodeDirectory, enablePatch);
} catch (IOException) { }
catch (IOException)
{
} }
} }
@@ -232,9 +259,11 @@ namespace Microsoft.Unity.VisualStudio.Editor {
] ]
}"; }";
private static void CreateLaunchFile(string vscodeDirectory, bool enablePatch) { private static void CreateLaunchFile(string vscodeDirectory, bool enablePatch)
{
var launchFile = IOPath.Combine(vscodeDirectory, "launch.json"); var launchFile = IOPath.Combine(vscodeDirectory, "launch.json");
if (File.Exists(launchFile)) { if (File.Exists(launchFile))
{
if (enablePatch) if (enablePatch)
PatchLaunchFile(launchFile); PatchLaunchFile(launchFile);
@@ -244,8 +273,10 @@ namespace Microsoft.Unity.VisualStudio.Editor {
File.WriteAllText(launchFile, DefaultLaunchFileContent); File.WriteAllText(launchFile, DefaultLaunchFileContent);
} }
private static void PatchLaunchFile(string launchFile) { private static void PatchLaunchFile(string launchFile)
try { {
try
{
const string configurationsKey = "configurations"; const string configurationsKey = "configurations";
const string typeKey = "type"; const string typeKey = "type";
@@ -253,7 +284,8 @@ namespace Microsoft.Unity.VisualStudio.Editor {
var launch = JSONNode.Parse(content); var launch = JSONNode.Parse(content);
var configurations = launch[configurationsKey] as JSONArray; var configurations = launch[configurationsKey] as JSONArray;
if (configurations == null) { if (configurations == null)
{
configurations = new JSONArray(); configurations = new JSONArray();
launch.Add(configurationsKey, configurations); launch.Add(configurationsKey, configurations);
} }
@@ -265,14 +297,18 @@ namespace Microsoft.Unity.VisualStudio.Editor {
configurations.Add(defaultContent[configurationsKey][0]); configurations.Add(defaultContent[configurationsKey][0]);
WriteAllTextFromJObject(launchFile, launch); WriteAllTextFromJObject(launchFile, launch);
} catch (Exception) { }
catch (Exception)
{
// do not fail if we cannot patch the launch.json file // do not fail if we cannot patch the launch.json file
} }
} }
private void CreateSettingsFile(string vscodeDirectory, bool enablePatch) { private void CreateSettingsFile(string vscodeDirectory, bool enablePatch)
{
var settingsFile = IOPath.Combine(vscodeDirectory, "settings.json"); var settingsFile = IOPath.Combine(vscodeDirectory, "settings.json");
if (File.Exists(settingsFile)) { if (File.Exists(settingsFile))
{
if (enablePatch) if (enablePatch)
PatchSettingsFile(settingsFile); PatchSettingsFile(settingsFile);
@@ -345,8 +381,10 @@ namespace Microsoft.Unity.VisualStudio.Editor {
File.WriteAllText(settingsFile, content); File.WriteAllText(settingsFile, content);
} }
private void PatchSettingsFile(string settingsFile) { private void PatchSettingsFile(string settingsFile)
try { {
try
{
const string excludesKey = "files.exclude"; const string excludesKey = "files.exclude";
const string solutionKey = "dotnet.defaultSolution"; const string solutionKey = "dotnet.defaultSolution";
@@ -361,7 +399,8 @@ namespace Microsoft.Unity.VisualStudio.Editor {
var patched = false; var patched = false;
// Remove files.exclude for solution+project files in the project root // Remove files.exclude for solution+project files in the project root
foreach (var exclude in excludes) { foreach (var exclude in excludes)
{
if (!bool.TryParse(exclude.Value, out var exc) || !exc) if (!bool.TryParse(exclude.Value, out var exc) || !exc)
continue; continue;
@@ -380,7 +419,8 @@ namespace Microsoft.Unity.VisualStudio.Editor {
// Check default solution // Check default solution
var defaultSolution = settings[solutionKey]; var defaultSolution = settings[solutionKey];
var solutionFile = IOPath.GetFileName(ProjectGenerator.SolutionFile()); var solutionFile = IOPath.GetFileName(ProjectGenerator.SolutionFile());
if (defaultSolution == null || defaultSolution.Value != solutionFile) { if (defaultSolution == null || defaultSolution.Value != solutionFile)
{
settings[solutionKey] = solutionFile; settings[solutionKey] = solutionFile;
patched = true; patched = true;
} }
@@ -392,7 +432,9 @@ namespace Microsoft.Unity.VisualStudio.Editor {
excludes.Remove(patch); excludes.Remove(patch);
WriteAllTextFromJObject(settingsFile, settings); WriteAllTextFromJObject(settingsFile, settings);
} catch (Exception) { }
catch (Exception)
{
// do not fail if we cannot patch the settings.json file // do not fail if we cannot patch the settings.json file
} }
} }
@@ -405,10 +447,12 @@ namespace Microsoft.Unity.VisualStudio.Editor {
} }
"; ";
private static void CreateRecommendedExtensionsFile(string vscodeDirectory, bool enablePatch) { private static void CreateRecommendedExtensionsFile(string vscodeDirectory, bool enablePatch)
{
// see https://tattoocoder.com/recommending-vscode-extensions-within-your-open-source-projects/ // see https://tattoocoder.com/recommending-vscode-extensions-within-your-open-source-projects/
var extensionFile = IOPath.Combine(vscodeDirectory, "extensions.json"); var extensionFile = IOPath.Combine(vscodeDirectory, "extensions.json");
if (File.Exists(extensionFile)) { if (File.Exists(extensionFile))
{
if (enablePatch) if (enablePatch)
PatchRecommendedExtensionsFile(extensionFile); PatchRecommendedExtensionsFile(extensionFile);
@@ -418,15 +462,18 @@ namespace Microsoft.Unity.VisualStudio.Editor {
File.WriteAllText(extensionFile, DefaultRecommendedExtensionsContent); File.WriteAllText(extensionFile, DefaultRecommendedExtensionsContent);
} }
private static void PatchRecommendedExtensionsFile(string extensionFile) { private static void PatchRecommendedExtensionsFile(string extensionFile)
try { {
try
{
const string recommendationsKey = "recommendations"; const string recommendationsKey = "recommendations";
var content = File.ReadAllText(extensionFile); var content = File.ReadAllText(extensionFile);
var extensions = JSONNode.Parse(content); var extensions = JSONNode.Parse(content);
var recommendations = extensions[recommendationsKey] as JSONArray; var recommendations = extensions[recommendationsKey] as JSONArray;
if (recommendations == null) { if (recommendations == null)
{
recommendations = new JSONArray(); recommendations = new JSONArray();
extensions.Add(recommendationsKey, recommendations); extensions.Add(recommendationsKey, recommendations);
} }
@@ -436,33 +483,39 @@ namespace Microsoft.Unity.VisualStudio.Editor {
recommendations.Add(MicrosoftUnityExtensionId); recommendations.Add(MicrosoftUnityExtensionId);
WriteAllTextFromJObject(extensionFile, extensions); WriteAllTextFromJObject(extensionFile, extensions);
} catch (Exception) { }
catch (Exception)
{
// do not fail if we cannot patch the extensions.json file // do not fail if we cannot patch the extensions.json file
} }
} }
private static void WriteAllTextFromJObject(string file, JSONNode node) { private static void WriteAllTextFromJObject(string file, JSONNode node)
{
using (var fs = File.Open(file, FileMode.Create)) using (var fs = File.Open(file, FileMode.Create))
using (var sw = new StreamWriter(fs)) { using (var sw = new StreamWriter(fs))
{
// Keep formatting/indent in sync with default contents // Keep formatting/indent in sync with default contents
sw.Write(node.ToString(aIndent: 4)); sw.Write(node.ToString(aIndent: 4));
} }
} }
private Process FindRunningCursorWithSolution(string solutionPath) { private Process FindRunningCursorWithSolution(string solutionPath)
{
var normalizedTargetPath = solutionPath.Replace('\\', '/').TrimEnd('/').ToLowerInvariant(); var normalizedTargetPath = solutionPath.Replace('\\', '/').TrimEnd('/').ToLowerInvariant();
#if UNITY_EDITOR_WIN #if UNITY_EDITOR_WIN
// Keep as is for Windows platform since path already includes drive letter // Keep as is for Windows platform since path already includes drive letter
#else #else
// Ensure path starts with / for macOS and Linux platforms // Ensure path starts with / for macOS and Linux platforms
if (!normalizedTargetPath.StartsWith("/")) { if (!normalizedTargetPath.StartsWith("/"))
{
normalizedTargetPath = "/" + normalizedTargetPath; normalizedTargetPath = "/" + normalizedTargetPath;
} }
#endif #endif
var processes = new List<Process>(); var processes = new List<Process>();
// Get process name list based on different operating systems // Get process name list based on different operating systems
#if UNITY_EDITOR_OSX #if UNITY_EDITOR_OSX
processes.AddRange(Process.GetProcessesByName("Cursor")); processes.AddRange(Process.GetProcessesByName("Cursor"));
@@ -473,19 +526,24 @@ namespace Microsoft.Unity.VisualStudio.Editor {
#else #else
processes.AddRange(Process.GetProcessesByName("cursor")); processes.AddRange(Process.GetProcessesByName("cursor"));
#endif #endif
foreach (var process in processes) { foreach (var process in processes)
try { {
try
{
var workspaces = ProcessRunner.GetProcessWorkspaces(process); var workspaces = ProcessRunner.GetProcessWorkspaces(process);
if (workspaces != null && workspaces.Length > 0) { if (workspaces != null && workspaces.Length > 0)
foreach (var workspace in workspaces) { {
foreach (var workspace in workspaces)
{
var normalizedWorkspaceDir = workspace.Replace('\\', '/').TrimEnd('/').ToLowerInvariant(); var normalizedWorkspaceDir = workspace.Replace('\\', '/').TrimEnd('/').ToLowerInvariant();
#if UNITY_EDITOR_WIN #if UNITY_EDITOR_WIN
// Keep as is for Windows platform // Keep as is for Windows platform
#else #else
// Ensure path starts with / for macOS and Linux platforms // Ensure path starts with / for macOS and Linux platforms
if (!normalizedWorkspaceDir.StartsWith("/")) { if (!normalizedWorkspaceDir.StartsWith("/"))
{
normalizedWorkspaceDir = "/" + normalizedWorkspaceDir; normalizedWorkspaceDir = "/" + normalizedWorkspaceDir;
} }
#endif #endif
@@ -499,7 +557,8 @@ namespace Microsoft.Unity.VisualStudio.Editor {
} }
} }
} }
catch (Exception ex) { catch (Exception ex)
{
Debug.LogError($"[Cursor] Error checking process: {ex}"); Debug.LogError($"[Cursor] Error checking process: {ex}");
continue; continue;
} }
@@ -507,37 +566,63 @@ namespace Microsoft.Unity.VisualStudio.Editor {
return null; return null;
} }
public override bool Open(string path, int line, int column, string solution) { private static string TryFindWorkspace(string directory)
{
var files = Directory.GetFiles(directory, "*.code-workspace", SearchOption.TopDirectoryOnly);
if (files.Length == 0 || files.Length > 1)
return null;
return files[0];
}
public override bool Open(string path, int line, int column, string solution)
{
line = Math.Max(1, line); line = Math.Max(1, line);
column = Math.Max(0, column); column = Math.Max(0, column);
var directory = IOPath.GetDirectoryName(solution); var directory = IOPath.GetDirectoryName(solution);
var application = Path; var application = Path;
var existingProcess = FindRunningCursorWithSolution(directory); var workspace = TryFindWorkspace(directory);
if (existingProcess != null) { // Use version-compatible null-coalescing for Unity 2019.4 (C# 7.3) support
try { #if UNITY_2020_2_OR_NEWER
var args = string.IsNullOrEmpty(path) ? workspace ??= directory;
$"--reuse-window \"{directory}\"" : #else
$"--reuse-window -g \"{path}\":{line}:{column}"; workspace = workspace ?? directory;
#endif
ProcessRunner.Start(ProcessStartInfoFor(application, args)); directory = workspace;
return true;
} if (EditorPrefs.GetBool(ReuseExistingWindowKey, false))
catch (Exception ex) { {
Debug.LogError($"[Cursor] Error using existing instance: {ex}"); var existingProcess = FindRunningCursorWithSolution(directory);
if (existingProcess != null)
{
try
{
var args = string.IsNullOrEmpty(path) ?
$"--reuse-window \"{directory}\"" :
$"--reuse-window -g \"{path}\":{line}:{column}";
ProcessRunner.Start(ProcessStartInfoFor(application, args));
return true;
}
catch (Exception ex)
{
Debug.LogError($"[Cursor] Error using existing instance: {ex}");
}
} }
} }
var newArgs = string.IsNullOrEmpty(path) ? var newArgs = string.IsNullOrEmpty(path) ?
$"--new-window \"{directory}\"" : $"--new-window \"{directory}\"" :
$"--new-window \"{directory}\" -g \"{path}\":{line}:{column}"; $"--new-window \"{directory}\" -g \"{path}\":{line}:{column}";
ProcessRunner.Start(ProcessStartInfoFor(application, newArgs)); ProcessRunner.Start(ProcessStartInfoFor(application, newArgs));
return true; return true;
} }
private static ProcessStartInfo ProcessStartInfoFor(string application, string arguments) { private static ProcessStartInfo ProcessStartInfoFor(string application, string arguments)
{
#if UNITY_EDITOR_OSX #if UNITY_EDITOR_OSX
// wrap with built-in OSX open feature // wrap with built-in OSX open feature
arguments = $"-n \"{application}\" --args {arguments}"; arguments = $"-n \"{application}\" --args {arguments}";
@@ -548,7 +633,8 @@ namespace Microsoft.Unity.VisualStudio.Editor {
#endif #endif
} }
public static void Initialize() { public static void Initialize()
{
} }
} }
} }

View File

@@ -44,8 +44,11 @@ namespace Microsoft.Unity.VisualStudio.Editor
[InitializeOnLoadMethod] [InitializeOnLoadMethod]
static void LegacyVisualStudioCodePackageDisabler() static void LegacyVisualStudioCodePackageDisabler()
{ {
// disable legacy Visual Studio Code packages #if UNITY_2021_1_OR_NEWER
var editor = CodeEditor.Editor.GetCodeEditorForPath("code.cmd"); var editor = CodeEditor.Editor.GetCodeEditorForPath("code.cmd");
#else
var editor = CodeEditor.CurrentEditor;
#endif
if (editor == null) if (editor == null)
return; return;
@@ -132,6 +135,16 @@ namespace Microsoft.Unity.VisualStudio.Editor
GUILayout.Label($"<size=10><color=grey>{package.displayName} v{package.version} enabled</color></size>", style); GUILayout.Label($"<size=10><color=grey>{package.displayName} v{package.version} enabled</color></size>", style);
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
if (installation is VisualStudioCursorInstallation)
{
var reuseWindow = EditorPrefs.GetBool(VisualStudioCursorInstallation.ReuseExistingWindowKey, false);
var newReuseWindow = EditorGUILayout.Toggle(new GUIContent("Reuse existing Cursor window", "When enabled, opens files in an existing Cursor window if found. When disabled, always opens a new window."), reuseWindow);
if (newReuseWindow != reuseWindow)
EditorPrefs.SetBool(VisualStudioCursorInstallation.ReuseExistingWindowKey, newReuseWindow);
EditorGUILayout.Space();
}
EditorGUILayout.LabelField("Generate .csproj files for:"); EditorGUILayout.LabelField("Generate .csproj files for:");
EditorGUI.indentLevel++; EditorGUI.indentLevel++;
SettingsButton(ProjectGenerationFlag.Embedded, "Embedded packages", "", installation); SettingsButton(ProjectGenerationFlag.Embedded, "Embedded packages", "", installation);
@@ -216,7 +229,9 @@ namespace Microsoft.Unity.VisualStudio.Editor
{ {
var editorPath = CodeEditor.CurrentEditorInstallation; var editorPath = CodeEditor.CurrentEditorInstallation;
if (!Discovery.TryDiscoverInstallation(editorPath, out var installation)) { // Performance optimization: Use cached installation lookup instead of discovering every time
if (!TryGetVisualStudioInstallationForPath(editorPath, lookupDiscoveredInstallations: true, out var installation))
{
Debug.LogWarning($"Visual Studio executable {editorPath} is not found. Please change your settings in Edit > Preferences > External Tools."); Debug.LogWarning($"Visual Studio executable {editorPath} is not found. Please change your settings in Edit > Preferences > External Tools.");
return false; return false;
} }
@@ -228,7 +243,8 @@ namespace Microsoft.Unity.VisualStudio.Editor
if (!IsProjectGeneratedFor(path, generator, out var missingFlag)) if (!IsProjectGeneratedFor(path, generator, out var missingFlag))
Debug.LogWarning($"You are trying to open {path} outside a generated project. This might cause problems with IntelliSense and debugging. To avoid this, you can change your .csproj preferences in Edit > Preferences > External Tools and enable {GetProjectGenerationFlagDescription(missingFlag)} generation."); Debug.LogWarning($"You are trying to open {path} outside a generated project. This might cause problems with IntelliSense and debugging. To avoid this, you can change your .csproj preferences in Edit > Preferences > External Tools and enable {GetProjectGenerationFlagDescription(missingFlag)} generation.");
var solution = GetOrGenerateSolutionFile(generator); // Performance optimization: Only sync if solution doesn't exist
var solution = GetOrGenerateSolutionFileIfNeeded(generator);
return installation.Open(path, line, column, solution); return installation.Open(path, line, column, solution);
} }
@@ -297,5 +313,15 @@ namespace Microsoft.Unity.VisualStudio.Editor
generator.Sync(); generator.Sync();
return generator.SolutionFile(); return generator.SolutionFile();
} }
// Performance optimization: Only sync if solution file doesn't exist
private static string GetOrGenerateSolutionFileIfNeeded(IGenerator generator)
{
if (!generator.HasSolutionBeenGenerated())
{
generator.Sync();
}
return generator.SolutionFile();
}
} }
} }

View File

@@ -1,8 +1,13 @@
# How to install ## How to install
<br> - Unity -> Window -> Package Manager
- Unity->Window->Package Manager<br> - Click "+" at the top left corner
- Click "+" left corner<br> - Add package from git URL
- Add package from git URL<br> - Insert `https://github.com/boxqkrtm/com.unity.ide.cursor.git`
- Insert <code>https://github.com/boxqkrtm/com.unity.ide.cursor.git</code><br> - Add
- Add<br>
- Done - Done
> **Important Notice for Users Updating from Older Versions**
> Starting from version **v2.0.24**, the package name has been changed from
> `com.unity.ide.cursor` to `com.boxqkrtm.ide.cursor` to prevent potential issues with Unity regarding attribution.
> Violating these attribution rules may trigger warnings in Unity.
> If you experience errors during the update, please remove the existing package before reinstalling the new one to avoid conflicts.

View File

@@ -2,13 +2,13 @@
"name": "com.boxqkrtm.ide.cursor", "name": "com.boxqkrtm.ide.cursor",
"displayName": "Cursor Editor", "displayName": "Cursor Editor",
"description": "Cursor editor integration for supporting Cursor as code editor for unity. Adds support for generating csproj files for intellisense purposes, auto discovery of installations, etc.", "description": "Cursor 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.25", "version": "2.0.27",
"unity": "2019.4", "unity": "2019.4",
"unityRelease": "25f1", "unityRelease": "25f1",
"dependencies": { "dependencies": {
"com.unity.test-framework": "1.1.9" "com.unity.test-framework": "1.1.9"
}, },
"_upm": { "_upm": {
"changelog": "Integration:\n\n- Add support for Cursor" "changelog": "Integration:\n\n- Add Multiple or Single Cursor Instance Options"
} }
} }