com.unity.ide.visualstudio@2.0.21

## [2.0.21] - 2023-09-05

Integration:

- Only disable the legacy `com.unity.ide.vscode` package going forward.
- Fix json parsing issues with specific non-UTF code pages.

Project generation:

- Target `netstandard2.1` instead of `netstandard2.0`.
- Set `defaultSolution` in `settings.json`.
- Remove `files.exclude` entries for root `csproj` and `sln` files in `settings.json` when needed.
- Add `vstuc` launch configuration to `launch.json` when needed.
- Add `visualstudiotoolsforunity.vstuc` entry to `extensions.json` when needed.
- You can prevent the package from patching those configuration files by creating a `.vscode/.vstupatchdisable` file.
This commit is contained in:
Unity Technologies
2023-09-05 00:00:00 +00:00
parent d1e4dd05ad
commit 9fed958a9e
10 changed files with 1727 additions and 104 deletions

View File

@@ -1,25 +1,34 @@
# Code Editor Package for Visual Studio # Code Editor Package for Visual Studio
## [2.0.21] - 2023-09-05
Integration:
- Only disable the legacy `com.unity.ide.vscode` package going forward.
- Fix json parsing issues with specific non-UTF code pages.
Project generation:
- Target `netstandard2.1` instead of `netstandard2.0`.
- Set `defaultSolution` in `settings.json`.
- Remove `files.exclude` entries for root `csproj` and `sln` files in `settings.json` when needed.
- Add `vstuc` launch configuration to `launch.json` when needed.
- Add `visualstudiotoolsforunity.vstuc` entry to `extensions.json` when needed.
- You can prevent the package from patching those configuration files by creating a `.vscode/.vstupatchdisable` file.
## [2.0.20] - 2023-06-27 ## [2.0.20] - 2023-06-27
Integration: Integration:
- Internal API refactoring. - Internal API refactoring.
## [2.0.19] - 2023-06-14
Integration:
- Add support for Visual Studio Code. - Add support for Visual Studio Code.
Project generation: Project generation:
- Add support for Sdk Style poject generation. - Add support for Sdk Style project generation.
- Fix an issue related to missing properties with 2021.3. - Fix an issue related to missing properties with 2021.3.
## [2.0.18] - 2023-03-17 ## [2.0.18] - 2023-03-17
Integration: Integration:

View File

@@ -46,9 +46,9 @@ namespace Microsoft.Unity.VisualStudio.Editor
headerBuilder.Append(@" <AppDesignerFolder>Properties</AppDesignerFolder>").Append(k_WindowsNewline); headerBuilder.Append(@" <AppDesignerFolder>Properties</AppDesignerFolder>").Append(k_WindowsNewline);
headerBuilder.Append(@" <AssemblyName>").Append(properties.AssemblyName).Append(@"</AssemblyName>").Append(k_WindowsNewline); headerBuilder.Append(@" <AssemblyName>").Append(properties.AssemblyName).Append(@"</AssemblyName>").Append(k_WindowsNewline);
// In the end, given we use NoConfig/NoStdLib (see below), hardcoding the target framework version will have no impact, even when targeting netstandard/net48 from Unity. // In the end, given we use NoConfig/NoStdLib (see below), hardcoding the target framework version will have no impact, even when targeting netstandard/net48 from Unity.
// But with SDK style we use netstandard2.0 (net471 for legacy), so 3rd party tools will not fail to work when .NETFW reference assemblies are not installed. // But with SDK style we use netstandard2.1 (net471 for legacy), so 3rd party tools will not fail to work when .NETFW reference assemblies are not installed.
// Unity already selected proper API surface through referenced DLLs for us. // Unity already selected proper API surface through referenced DLLs for us.
headerBuilder.Append(@" <TargetFramework>netstandard2.0</TargetFramework>").Append(k_WindowsNewline); headerBuilder.Append(@" <TargetFramework>netstandard2.1</TargetFramework>").Append(k_WindowsNewline);
headerBuilder.Append(@" <BaseDirectory>.</BaseDirectory>").Append(k_WindowsNewline); headerBuilder.Append(@" <BaseDirectory>.</BaseDirectory>").Append(k_WindowsNewline);
headerBuilder.Append(@" </PropertyGroup>").Append(k_WindowsNewline); headerBuilder.Append(@" </PropertyGroup>").Append(k_WindowsNewline);

1434
Editor/SimpleJSON.cs Normal file

File diff suppressed because it is too large Load Diff

11
Editor/SimpleJSON.cs.meta Normal file
View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ed8e7c93c781e48df93d063a3c0d2972
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -10,9 +10,8 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using UnityEngine; using UnityEngine;
using SimpleJSON;
using IOPath = System.IO.Path; using IOPath = System.IO.Path;
using System.Runtime.InteropServices;
using System.Text;
namespace Microsoft.Unity.VisualStudio.Editor namespace Microsoft.Unity.VisualStudio.Editor
{ {
@@ -44,7 +43,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
return null; return null;
return Directory return Directory
.EnumerateDirectories(extensionsPath, "visualstudiotoolsforunity.vstuc*") // publisherid.extensionid .EnumerateDirectories(extensionsPath, $"{MicrosoftUnityExtensionId}*") // publisherid.extensionid
.OrderByDescending(n => n) .OrderByDescending(n => n)
.FirstOrDefault(); .FirstOrDefault();
} }
@@ -170,7 +169,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
} }
#if UNITY_EDITOR_LINUX #if UNITY_EDITOR_LINUX
[DllImport ("libc")] [System.Runtime.InteropServices.DllImport ("libc")]
private static extern int readlink(string path, byte[] buffer, int buflen); private static extern int readlink(string path, byte[] buffer, int buflen);
internal static string GetRealPath(string path) internal static string GetRealPath(string path)
@@ -193,123 +192,270 @@ namespace Microsoft.Unity.VisualStudio.Editor
{ {
try try
{ {
// see https://tattoocoder.com/recommending-vscode-extensions-within-your-open-source-projects/
var vscodeDirectory = IOPath.Combine(projectDirectory.NormalizePathSeparators(), ".vscode"); var vscodeDirectory = IOPath.Combine(projectDirectory.NormalizePathSeparators(), ".vscode");
Directory.CreateDirectory(vscodeDirectory); Directory.CreateDirectory(vscodeDirectory);
CreateRecommendedExtensionsFile(vscodeDirectory); var enablePatch = !File.Exists(IOPath.Combine(vscodeDirectory, ".vstupatchdisable"));
CreateSettingsFile(vscodeDirectory);
CreateLaunchFile(vscodeDirectory); CreateRecommendedExtensionsFile(vscodeDirectory, enablePatch);
CreateSettingsFile(vscodeDirectory, enablePatch);
CreateLaunchFile(vscodeDirectory, enablePatch);
} }
catch (IOException) catch (IOException)
{ {
} }
} }
private static void CreateLaunchFile(string vscodeDirectory) private const string DefaultLaunchFileContent = @"{
{
var launchFile = IOPath.Combine(vscodeDirectory, "launch.json");
if (File.Exists(launchFile))
return;
const string content = @"{
""version"": ""0.2.0"", ""version"": ""0.2.0"",
""configurations"": [ ""configurations"": [
{ {
""name"": ""Attach to Unity"", ""name"": ""Attach to Unity"",
""type"": ""vstuc"", ""type"": ""vstuc"",
""request"": ""attach"", ""request"": ""attach""
} }
] ]
}"; }";
File.WriteAllText(launchFile, content); private static void CreateLaunchFile(string vscodeDirectory, bool enablePatch)
{
var launchFile = IOPath.Combine(vscodeDirectory, "launch.json");
if (File.Exists(launchFile))
{
if (enablePatch)
PatchLaunchFile(launchFile);
return;
}
File.WriteAllText(launchFile, DefaultLaunchFileContent);
} }
private static void CreateSettingsFile(string vscodeDirectory) private static void PatchLaunchFile(string launchFile)
{
try
{
const string configurationsKey = "configurations";
const string typeKey = "type";
var content = File.ReadAllText(launchFile);
var launch = JSONNode.Parse(content);
var configurations = launch[configurationsKey] as JSONArray;
if (configurations == null)
{
configurations = new JSONArray();
launch.Add(configurationsKey, configurations);
}
if (configurations.Linq.Any(entry => entry.Value[typeKey].Value == "vstuc"))
return;
var defaultContent = JSONNode.Parse(DefaultLaunchFileContent);
configurations.Add(defaultContent[configurationsKey][0]);
WriteAllTextFromJObject(launchFile, launch);
}
catch (Exception)
{
// do not fail if we cannot patch the launch.json file
}
}
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))
return; {
if (enablePatch)
PatchSettingsFile(settingsFile);
const string content = @"{ return;
""files.exclude"": }
{
""**/.DS_Store"":true, const string excludes = @" ""files.exclude"": {
""**/.git"":true, ""**/.DS_Store"": true,
""**/.vs"":true, ""**/.git"": true,
""**/.gitmodules"":true, ""**/.vs"": true,
""**/.vsconfig"":true, ""**/.gitmodules"": true,
""**/*.booproj"":true, ""**/.vsconfig"": true,
""**/*.pidb"":true, ""**/*.booproj"": true,
""**/*.suo"":true, ""**/*.pidb"": true,
""**/*.user"":true, ""**/*.suo"": true,
""**/*.userprefs"":true, ""**/*.user"": true,
""**/*.unityproj"":true, ""**/*.userprefs"": true,
""**/*.dll"":true, ""**/*.unityproj"": true,
""**/*.exe"":true, ""**/*.dll"": true,
""**/*.pdf"":true, ""**/*.exe"": true,
""**/*.mid"":true, ""**/*.pdf"": true,
""**/*.midi"":true, ""**/*.mid"": true,
""**/*.wav"":true, ""**/*.midi"": true,
""**/*.gif"":true, ""**/*.wav"": true,
""**/*.ico"":true, ""**/*.gif"": true,
""**/*.jpg"":true, ""**/*.ico"": true,
""**/*.jpeg"":true, ""**/*.jpg"": true,
""**/*.png"":true, ""**/*.jpeg"": true,
""**/*.psd"":true, ""**/*.png"": true,
""**/*.tga"":true, ""**/*.psd"": true,
""**/*.tif"":true, ""**/*.tga"": true,
""**/*.tiff"":true, ""**/*.tif"": true,
""**/*.3ds"":true, ""**/*.tiff"": true,
""**/*.3DS"":true, ""**/*.3ds"": true,
""**/*.fbx"":true, ""**/*.3DS"": true,
""**/*.FBX"":true, ""**/*.fbx"": true,
""**/*.lxo"":true, ""**/*.FBX"": true,
""**/*.LXO"":true, ""**/*.lxo"": true,
""**/*.ma"":true, ""**/*.LXO"": true,
""**/*.MA"":true, ""**/*.ma"": true,
""**/*.obj"":true, ""**/*.MA"": true,
""**/*.OBJ"":true, ""**/*.obj"": true,
""**/*.asset"":true, ""**/*.OBJ"": true,
""**/*.cubemap"":true, ""**/*.asset"": true,
""**/*.flare"":true, ""**/*.cubemap"": true,
""**/*.mat"":true, ""**/*.flare"": true,
""**/*.meta"":true, ""**/*.mat"": true,
""**/*.prefab"":true, ""**/*.meta"": true,
""**/*.unity"":true, ""**/*.prefab"": true,
""build/"":true, ""**/*.unity"": true,
""Build/"":true, ""build/"": true,
""Library/"":true, ""Build/"": true,
""library/"":true, ""Library/"": true,
""obj/"":true, ""library/"": true,
""Obj/"":true, ""obj/"": true,
""Logs/"":true, ""Obj/"": true,
""logs/"":true, ""Logs/"": true,
""ProjectSettings/"":true, ""logs/"": true,
""UserSettings/"":true, ""ProjectSettings/"": true,
""temp/"":true, ""UserSettings/"": true,
""Temp/"":true ""temp/"": true,
}, ""Temp/"": true
""omnisharp.enableRoslynAnalyzers"": true }";
var content = @"{
" + excludes + @",
""dotnet.defaultSolution"": """ + IOPath.GetFileName(ProjectGenerator.SolutionFile()) + @"""
}"; }";
File.WriteAllText(settingsFile, content); File.WriteAllText(settingsFile, content);
} }
private static void CreateRecommendedExtensionsFile(string vscodeDirectory) private void PatchSettingsFile(string settingsFile)
{ {
var extensionFile = IOPath.Combine(vscodeDirectory, "extensions.json"); try
if (File.Exists(extensionFile)) {
return; const string excludesKey = "files.exclude";
const string solutionKey = "dotnet.defaultSolution";
const string content = @"{ var content = File.ReadAllText(settingsFile);
var settings = JSONNode.Parse(content);
var excludes = settings[excludesKey] as JSONObject;
if (excludes == null)
return;
var patchList = new List<string>();
var patched = false;
// Remove files.exclude for solution+project files in the project root
foreach (var exclude in excludes)
{
if (!bool.TryParse(exclude.Value, out var exc) || !exc)
continue;
var key = exclude.Key;
if (!key.EndsWith(".sln") && !key.EndsWith(".csproj"))
continue;
if (!Regex.IsMatch(key, "^(\\*\\*[\\\\\\/])?\\*\\.(sln|csproj)$"))
continue;
patchList.Add(key);
patched = true;
}
// Check default solution
var defaultSolution = settings[solutionKey];
var solutionFile = IOPath.GetFileName(ProjectGenerator.SolutionFile());
if (defaultSolution == null || defaultSolution.Value != solutionFile)
{
settings[solutionKey] = solutionFile;
patched = true;
}
if (!patched)
return;
foreach (var patch in patchList)
excludes.Remove(patch);
WriteAllTextFromJObject(settingsFile, settings);
}
catch (Exception)
{
// do not fail if we cannot patch the settings.json file
}
}
private const string MicrosoftUnityExtensionId = "visualstudiotoolsforunity.vstuc";
private const string DefaultRecommendedExtensionsContent = @"{
""recommendations"": [ ""recommendations"": [
""visualstudiotoolsforunity.vstuc"" """+ MicrosoftUnityExtensionId + @"""
] ]
} }
"; ";
File.WriteAllText(extensionFile, content);
private static void CreateRecommendedExtensionsFile(string vscodeDirectory, bool enablePatch)
{
// see https://tattoocoder.com/recommending-vscode-extensions-within-your-open-source-projects/
var extensionFile = IOPath.Combine(vscodeDirectory, "extensions.json");
if (File.Exists(extensionFile))
{
if (enablePatch)
PatchRecommendedExtensionsFile(extensionFile);
return;
}
File.WriteAllText(extensionFile, DefaultRecommendedExtensionsContent);
}
private static void PatchRecommendedExtensionsFile(string extensionFile)
{
try
{
const string recommendationsKey = "recommendations";
var content = File.ReadAllText(extensionFile);
var extensions = JSONNode.Parse(content);
var recommendations = extensions[recommendationsKey] as JSONArray;
if (recommendations == null)
{
recommendations = new JSONArray();
extensions.Add(recommendationsKey, recommendations);
}
if (recommendations.Linq.Any(entry => entry.Value.Value == MicrosoftUnityExtensionId))
return;
recommendations.Add(MicrosoftUnityExtensionId);
WriteAllTextFromJObject(extensionFile, extensions);
}
catch (Exception)
{
// do not fail if we cannot patch the extensions.json file
}
}
private static void WriteAllTextFromJObject(string file, JSONNode node)
{
using (var fs = File.Open(file, FileMode.Create))
using (var sw = new StreamWriter(fs))
{
// Keep formatting/indent in sync with default contents
sw.Write(node.ToString(aIndent: 4));
}
} }
public override bool Open(string path, int line, int column, string solution) public override bool Open(string path, int line, int column, string solution)

View File

@@ -55,6 +55,12 @@ namespace Microsoft.Unity.VisualStudio.Editor
if (editor is VisualStudioEditor) if (editor is VisualStudioEditor)
return; return;
// only disable the com.unity.ide.vscode package
var assembly = editor.GetType().Assembly;
var assemblyName = assembly.GetName().Name;
if (assemblyName != "Unity.VSCode.Editor")
return;
CodeEditor.Unregister(editor); CodeEditor.Unregister(editor);
} }
#endif #endif

View File

@@ -228,7 +228,24 @@ namespace Microsoft.Unity.VisualStudio.Editor
if (string.IsNullOrWhiteSpace(progpath)) if (string.IsNullOrWhiteSpace(progpath))
return Enumerable.Empty<VisualStudioInstallation>(); return Enumerable.Empty<VisualStudioInstallation>();
var result = ProcessRunner.StartAndWaitForExit(progpath, "-prerelease -format json -utf8"); 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<VisualStudioInstallation> QueryVsWhere(string progpath, string arguments)
{
var result = ProcessRunner.StartAndWaitForExit(progpath, arguments);
if (!result.Success) if (!result.Success)
throw new Exception($"Failure while running vswhere: {result.Error}"); throw new Exception($"Failure while running vswhere: {result.Error}");

View File

@@ -2,25 +2,25 @@
"name": "com.unity.ide.visualstudio", "name": "com.unity.ide.visualstudio",
"displayName": "Visual Studio Editor", "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.", "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.",
"version": "2.0.20", "version": "2.0.21",
"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"
}, },
"relatedPackages": { "relatedPackages": {
"com.unity.ide.visualstudio.tests": "2.0.20" "com.unity.ide.visualstudio.tests": "2.0.21"
}, },
"_upm": { "_upm": {
"changelog": "Integration:\n\n- Internal API refactoring." "changelog": "Integration:\n\n- Only disable the legacy `com.unity.ide.vscode` package going forward.\n- Fix json parsing issues with specific non-UTF code pages.\n\nProject generation:\n\n- Target `netstandard2.1` instead of `netstandard2.0`.\n- Set `defaultSolution` in `settings.json`.\n- Remove `files.exclude` entries for root `csproj` and `sln` files in `settings.json` when needed.\n- Add `vstuc` launch configuration to `launch.json` when needed.\n- Add `visualstudiotoolsforunity.vstuc` entry to `extensions.json` when needed.\n- You can prevent the package from patching those configuration files by creating a `.vscode/.vstupatchdisable` file."
}, },
"upmCi": { "upmCi": {
"footprint": "7d769a8558c7768417b16fc2ac8477cf69234049" "footprint": "33ecd838a074080a82176ca86b21c837ba396a28"
}, },
"documentationUrl": "https://docs.unity3d.com/Packages/com.unity.ide.visualstudio@2.0/manual/index.html", "documentationUrl": "https://docs.unity3d.com/Packages/com.unity.ide.visualstudio@2.0/manual/index.html",
"repository": { "repository": {
"url": "https://github.cds.internal.unity3d.com/unity/com.unity.ide.visualstudio.git", "url": "https://github.cds.internal.unity3d.com/unity/com.unity.ide.visualstudio.git",
"type": "git", "type": "git",
"revision": "b7bf23d23806ac75645bfa12acadcfc11468a383" "revision": "eb2b500b99f6429d3d9dcb8acbcbd366dfbb42be"
} }
} }