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

@@ -46,9 +46,9 @@ namespace Microsoft.Unity.VisualStudio.Editor
headerBuilder.Append(@" <AppDesignerFolder>Properties</AppDesignerFolder>").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.
// 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.
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(@" </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.Text.RegularExpressions;
using UnityEngine;
using SimpleJSON;
using IOPath = System.IO.Path;
using System.Runtime.InteropServices;
using System.Text;
namespace Microsoft.Unity.VisualStudio.Editor
{
@@ -44,7 +43,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
return null;
return Directory
.EnumerateDirectories(extensionsPath, "visualstudiotoolsforunity.vstuc*") // publisherid.extensionid
.EnumerateDirectories(extensionsPath, $"{MicrosoftUnityExtensionId}*") // publisherid.extensionid
.OrderByDescending(n => n)
.FirstOrDefault();
}
@@ -170,7 +169,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
}
#if UNITY_EDITOR_LINUX
[DllImport ("libc")]
[System.Runtime.InteropServices.DllImport ("libc")]
private static extern int readlink(string path, byte[] buffer, int buflen);
internal static string GetRealPath(string path)
@@ -193,123 +192,270 @@ namespace Microsoft.Unity.VisualStudio.Editor
{
try
{
// see https://tattoocoder.com/recommending-vscode-extensions-within-your-open-source-projects/
var vscodeDirectory = IOPath.Combine(projectDirectory.NormalizePathSeparators(), ".vscode");
Directory.CreateDirectory(vscodeDirectory);
CreateRecommendedExtensionsFile(vscodeDirectory);
CreateSettingsFile(vscodeDirectory);
CreateLaunchFile(vscodeDirectory);
var enablePatch = !File.Exists(IOPath.Combine(vscodeDirectory, ".vstupatchdisable"));
CreateRecommendedExtensionsFile(vscodeDirectory, enablePatch);
CreateSettingsFile(vscodeDirectory, enablePatch);
CreateLaunchFile(vscodeDirectory, enablePatch);
}
catch (IOException)
{
}
}
private static void CreateLaunchFile(string vscodeDirectory)
{
var launchFile = IOPath.Combine(vscodeDirectory, "launch.json");
if (File.Exists(launchFile))
return;
const string content = @"{
private const string DefaultLaunchFileContent = @"{
""version"": ""0.2.0"",
""configurations"": [
{
""name"": ""Attach to Unity"",
""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");
if (File.Exists(settingsFile))
return;
{
if (enablePatch)
PatchSettingsFile(settingsFile);
const string content = @"{
""files.exclude"":
{
""**/.DS_Store"":true,
""**/.git"":true,
""**/.vs"":true,
""**/.gitmodules"":true,
""**/.vsconfig"":true,
""**/*.booproj"":true,
""**/*.pidb"":true,
""**/*.suo"":true,
""**/*.user"":true,
""**/*.userprefs"":true,
""**/*.unityproj"":true,
""**/*.dll"":true,
""**/*.exe"":true,
""**/*.pdf"":true,
""**/*.mid"":true,
""**/*.midi"":true,
""**/*.wav"":true,
""**/*.gif"":true,
""**/*.ico"":true,
""**/*.jpg"":true,
""**/*.jpeg"":true,
""**/*.png"":true,
""**/*.psd"":true,
""**/*.tga"":true,
""**/*.tif"":true,
""**/*.tiff"":true,
""**/*.3ds"":true,
""**/*.3DS"":true,
""**/*.fbx"":true,
""**/*.FBX"":true,
""**/*.lxo"":true,
""**/*.LXO"":true,
""**/*.ma"":true,
""**/*.MA"":true,
""**/*.obj"":true,
""**/*.OBJ"":true,
""**/*.asset"":true,
""**/*.cubemap"":true,
""**/*.flare"":true,
""**/*.mat"":true,
""**/*.meta"":true,
""**/*.prefab"":true,
""**/*.unity"":true,
""build/"":true,
""Build/"":true,
""Library/"":true,
""library/"":true,
""obj/"":true,
""Obj/"":true,
""Logs/"":true,
""logs/"":true,
""ProjectSettings/"":true,
""UserSettings/"":true,
""temp/"":true,
""Temp/"":true
},
""omnisharp.enableRoslynAnalyzers"": true
return;
}
const string excludes = @" ""files.exclude"": {
""**/.DS_Store"": true,
""**/.git"": true,
""**/.vs"": true,
""**/.gitmodules"": true,
""**/.vsconfig"": true,
""**/*.booproj"": true,
""**/*.pidb"": true,
""**/*.suo"": true,
""**/*.user"": true,
""**/*.userprefs"": true,
""**/*.unityproj"": true,
""**/*.dll"": true,
""**/*.exe"": true,
""**/*.pdf"": true,
""**/*.mid"": true,
""**/*.midi"": true,
""**/*.wav"": true,
""**/*.gif"": true,
""**/*.ico"": true,
""**/*.jpg"": true,
""**/*.jpeg"": true,
""**/*.png"": true,
""**/*.psd"": true,
""**/*.tga"": true,
""**/*.tif"": true,
""**/*.tiff"": true,
""**/*.3ds"": true,
""**/*.3DS"": true,
""**/*.fbx"": true,
""**/*.FBX"": true,
""**/*.lxo"": true,
""**/*.LXO"": true,
""**/*.ma"": true,
""**/*.MA"": true,
""**/*.obj"": true,
""**/*.OBJ"": true,
""**/*.asset"": true,
""**/*.cubemap"": true,
""**/*.flare"": true,
""**/*.mat"": true,
""**/*.meta"": true,
""**/*.prefab"": true,
""**/*.unity"": true,
""build/"": true,
""Build/"": true,
""Library/"": true,
""library/"": true,
""obj/"": true,
""Obj/"": true,
""Logs/"": true,
""logs/"": true,
""ProjectSettings/"": true,
""UserSettings/"": true,
""temp/"": true,
""Temp/"": true
}";
var content = @"{
" + excludes + @",
""dotnet.defaultSolution"": """ + IOPath.GetFileName(ProjectGenerator.SolutionFile()) + @"""
}";
File.WriteAllText(settingsFile, content);
}
private static void CreateRecommendedExtensionsFile(string vscodeDirectory)
private void PatchSettingsFile(string settingsFile)
{
var extensionFile = IOPath.Combine(vscodeDirectory, "extensions.json");
if (File.Exists(extensionFile))
return;
try
{
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"": [
""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)

View File

@@ -55,6 +55,12 @@ namespace Microsoft.Unity.VisualStudio.Editor
if (editor is VisualStudioEditor)
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);
}
#endif

View File

@@ -228,7 +228,24 @@ namespace Microsoft.Unity.VisualStudio.Editor
if (string.IsNullOrWhiteSpace(progpath))
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)
throw new Exception($"Failure while running vswhere: {result.Error}");