diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0a694c9..898f78c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,25 +1,34 @@
# 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
Integration:
- Internal API refactoring.
-
-
-
-## [2.0.19] - 2023-06-14
-
-Integration:
-
- Add support for Visual Studio Code.
-
+
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.
-
## [2.0.18] - 2023-03-17
Integration:
diff --git a/Editor/COMIntegration/Release/COMIntegration.exe b/Editor/COMIntegration/Release/COMIntegration.exe
index 4622082..5788337 100644
Binary files a/Editor/COMIntegration/Release/COMIntegration.exe and b/Editor/COMIntegration/Release/COMIntegration.exe differ
diff --git a/Editor/Plugins/AppleEventIntegration.bundle/Contents/MacOS/AppleEventIntegration b/Editor/Plugins/AppleEventIntegration.bundle/Contents/MacOS/AppleEventIntegration
index 038318e..6965b8c 100644
Binary files a/Editor/Plugins/AppleEventIntegration.bundle/Contents/MacOS/AppleEventIntegration and b/Editor/Plugins/AppleEventIntegration.bundle/Contents/MacOS/AppleEventIntegration differ
diff --git a/Editor/ProjectGeneration/SdkStyleProjectGeneration.cs b/Editor/ProjectGeneration/SdkStyleProjectGeneration.cs
index f458414..a860645 100644
--- a/Editor/ProjectGeneration/SdkStyleProjectGeneration.cs
+++ b/Editor/ProjectGeneration/SdkStyleProjectGeneration.cs
@@ -46,9 +46,9 @@ namespace Microsoft.Unity.VisualStudio.Editor
headerBuilder.Append(@" Properties").Append(k_WindowsNewline);
headerBuilder.Append(@" ").Append(properties.AssemblyName).Append(@"").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(@" netstandard2.0").Append(k_WindowsNewline);
+ headerBuilder.Append(@" netstandard2.1").Append(k_WindowsNewline);
headerBuilder.Append(@" .").Append(k_WindowsNewline);
headerBuilder.Append(@" ").Append(k_WindowsNewline);
diff --git a/Editor/SimpleJSON.cs b/Editor/SimpleJSON.cs
new file mode 100644
index 0000000..7451961
--- /dev/null
+++ b/Editor/SimpleJSON.cs
@@ -0,0 +1,1434 @@
+/* * * * *
+ * A simple JSON Parser / builder
+ * ------------------------------
+ *
+ * It mainly has been written as a simple JSON parser. It can build a JSON string
+ * from the node-tree, or generate a node tree from any valid JSON string.
+ *
+ * Written by Bunny83
+ * 2012-06-09
+ *
+ * Changelog now external. See Changelog.txt
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2012-2022 Markus Göbel (Bunny83)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * * * * */
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+
+namespace SimpleJSON
+{
+ internal enum JSONNodeType
+ {
+ Array = 1,
+ Object = 2,
+ String = 3,
+ Number = 4,
+ NullValue = 5,
+ Boolean = 6,
+ None = 7,
+ Custom = 0xFF,
+ }
+ internal enum JSONTextMode
+ {
+ Compact,
+ Indent
+ }
+
+ internal abstract partial class JSONNode
+ {
+ #region Enumerators
+ internal struct Enumerator
+ {
+ private enum Type { None, Array, Object }
+ private Type type;
+ private Dictionary.Enumerator m_Object;
+ private List.Enumerator m_Array;
+ public bool IsValid { get { return type != Type.None; } }
+ public Enumerator(List.Enumerator aArrayEnum)
+ {
+ type = Type.Array;
+ m_Object = default(Dictionary.Enumerator);
+ m_Array = aArrayEnum;
+ }
+ public Enumerator(Dictionary.Enumerator aDictEnum)
+ {
+ type = Type.Object;
+ m_Object = aDictEnum;
+ m_Array = default(List.Enumerator);
+ }
+ public KeyValuePair Current
+ {
+ get
+ {
+ if (type == Type.Array)
+ return new KeyValuePair(string.Empty, m_Array.Current);
+ else if (type == Type.Object)
+ return m_Object.Current;
+ return new KeyValuePair(string.Empty, null);
+ }
+ }
+ public bool MoveNext()
+ {
+ if (type == Type.Array)
+ return m_Array.MoveNext();
+ else if (type == Type.Object)
+ return m_Object.MoveNext();
+ return false;
+ }
+ }
+ internal struct ValueEnumerator
+ {
+ private Enumerator m_Enumerator;
+ public ValueEnumerator(List.Enumerator aArrayEnum) : this(new Enumerator(aArrayEnum)) { }
+ public ValueEnumerator(Dictionary.Enumerator aDictEnum) : this(new Enumerator(aDictEnum)) { }
+ public ValueEnumerator(Enumerator aEnumerator) { m_Enumerator = aEnumerator; }
+ public JSONNode Current { get { return m_Enumerator.Current.Value; } }
+ public bool MoveNext() { return m_Enumerator.MoveNext(); }
+ public ValueEnumerator GetEnumerator() { return this; }
+ }
+ internal struct KeyEnumerator
+ {
+ private Enumerator m_Enumerator;
+ public KeyEnumerator(List.Enumerator aArrayEnum) : this(new Enumerator(aArrayEnum)) { }
+ public KeyEnumerator(Dictionary.Enumerator aDictEnum) : this(new Enumerator(aDictEnum)) { }
+ public KeyEnumerator(Enumerator aEnumerator) { m_Enumerator = aEnumerator; }
+ public string Current { get { return m_Enumerator.Current.Key; } }
+ public bool MoveNext() { return m_Enumerator.MoveNext(); }
+ public KeyEnumerator GetEnumerator() { return this; }
+ }
+
+ internal class LinqEnumerator : IEnumerator>, IEnumerable>
+ {
+ private JSONNode m_Node;
+ private Enumerator m_Enumerator;
+ internal LinqEnumerator(JSONNode aNode)
+ {
+ m_Node = aNode;
+ if (m_Node != null)
+ m_Enumerator = m_Node.GetEnumerator();
+ }
+ public KeyValuePair Current { get { return m_Enumerator.Current; } }
+ object IEnumerator.Current { get { return m_Enumerator.Current; } }
+ public bool MoveNext() { return m_Enumerator.MoveNext(); }
+
+ public void Dispose()
+ {
+ m_Node = null;
+ m_Enumerator = new Enumerator();
+ }
+
+ public IEnumerator> GetEnumerator()
+ {
+ return new LinqEnumerator(m_Node);
+ }
+
+ public void Reset()
+ {
+ if (m_Node != null)
+ m_Enumerator = m_Node.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return new LinqEnumerator(m_Node);
+ }
+ }
+
+ #endregion Enumerators
+
+ #region common interface
+
+ public static bool forceASCII = false; // Use Unicode by default
+ public static bool longAsString = false; // lazy creator creates a JSONString instead of JSONNumber
+ public static bool allowLineComments = true; // allow "//"-style comments at the end of a line
+
+ public abstract JSONNodeType Tag { get; }
+
+ public virtual JSONNode this[int aIndex] { get { return null; } set { } }
+
+ public virtual JSONNode this[string aKey] { get { return null; } set { } }
+
+ public virtual string Value { get { return ""; } set { } }
+
+ public virtual int Count { get { return 0; } }
+
+ public virtual bool IsNumber { get { return false; } }
+ public virtual bool IsString { get { return false; } }
+ public virtual bool IsBoolean { get { return false; } }
+ public virtual bool IsNull { get { return false; } }
+ public virtual bool IsArray { get { return false; } }
+ public virtual bool IsObject { get { return false; } }
+
+ public virtual bool Inline { get { return false; } set { } }
+
+ public virtual void Add(string aKey, JSONNode aItem)
+ {
+ }
+ public virtual void Add(JSONNode aItem)
+ {
+ Add("", aItem);
+ }
+
+ public virtual JSONNode Remove(string aKey)
+ {
+ return null;
+ }
+
+ public virtual JSONNode Remove(int aIndex)
+ {
+ return null;
+ }
+
+ public virtual JSONNode Remove(JSONNode aNode)
+ {
+ return aNode;
+ }
+ public virtual void Clear() { }
+
+ public virtual JSONNode Clone()
+ {
+ return null;
+ }
+
+ public virtual IEnumerable Children
+ {
+ get
+ {
+ yield break;
+ }
+ }
+
+ public IEnumerable DeepChildren
+ {
+ get
+ {
+ foreach (var C in Children)
+ foreach (var D in C.DeepChildren)
+ yield return D;
+ }
+ }
+
+ public virtual bool HasKey(string aKey)
+ {
+ return false;
+ }
+
+ public virtual JSONNode GetValueOrDefault(string aKey, JSONNode aDefault)
+ {
+ return aDefault;
+ }
+
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+ WriteToStringBuilder(sb, 0, 0, JSONTextMode.Compact);
+ return sb.ToString();
+ }
+
+ public virtual string ToString(int aIndent)
+ {
+ StringBuilder sb = new StringBuilder();
+ WriteToStringBuilder(sb, 0, aIndent, JSONTextMode.Indent);
+ return sb.ToString();
+ }
+ internal abstract void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode);
+
+ public abstract Enumerator GetEnumerator();
+ public IEnumerable> Linq { get { return new LinqEnumerator(this); } }
+ public KeyEnumerator Keys { get { return new KeyEnumerator(GetEnumerator()); } }
+ public ValueEnumerator Values { get { return new ValueEnumerator(GetEnumerator()); } }
+
+ #endregion common interface
+
+ #region typecasting properties
+
+
+ public virtual double AsDouble
+ {
+ get
+ {
+ double v = 0.0;
+ if (double.TryParse(Value, NumberStyles.Float, CultureInfo.InvariantCulture, out v))
+ return v;
+ return 0.0;
+ }
+ set
+ {
+ Value = value.ToString(CultureInfo.InvariantCulture);
+ }
+ }
+
+ public virtual int AsInt
+ {
+ get { return (int)AsDouble; }
+ set { AsDouble = value; }
+ }
+
+ public virtual float AsFloat
+ {
+ get { return (float)AsDouble; }
+ set { AsDouble = value; }
+ }
+
+ public virtual bool AsBool
+ {
+ get
+ {
+ bool v = false;
+ if (bool.TryParse(Value, out v))
+ return v;
+ return !string.IsNullOrEmpty(Value);
+ }
+ set
+ {
+ Value = (value) ? "true" : "false";
+ }
+ }
+
+ public virtual long AsLong
+ {
+ get
+ {
+ long val = 0;
+ if (long.TryParse(Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out val))
+ return val;
+ return 0L;
+ }
+ set
+ {
+ Value = value.ToString(CultureInfo.InvariantCulture);
+ }
+ }
+
+ public virtual ulong AsULong
+ {
+ get
+ {
+ ulong val = 0;
+ if (ulong.TryParse(Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out val))
+ return val;
+ return 0;
+ }
+ set
+ {
+ Value = value.ToString(CultureInfo.InvariantCulture);
+ }
+ }
+
+ public virtual JSONArray AsArray
+ {
+ get
+ {
+ return this as JSONArray;
+ }
+ }
+
+ public virtual JSONObject AsObject
+ {
+ get
+ {
+ return this as JSONObject;
+ }
+ }
+
+
+ #endregion typecasting properties
+
+ #region operators
+
+ public static implicit operator JSONNode(string s)
+ {
+ return (s == null) ? (JSONNode)JSONNull.CreateOrGet() : new JSONString(s);
+ }
+ public static implicit operator string(JSONNode d)
+ {
+ return (d == null) ? null : d.Value;
+ }
+
+ public static implicit operator JSONNode(double n)
+ {
+ return new JSONNumber(n);
+ }
+ public static implicit operator double(JSONNode d)
+ {
+ return (d == null) ? 0 : d.AsDouble;
+ }
+
+ public static implicit operator JSONNode(float n)
+ {
+ return new JSONNumber(n);
+ }
+ public static implicit operator float(JSONNode d)
+ {
+ return (d == null) ? 0 : d.AsFloat;
+ }
+
+ public static implicit operator JSONNode(int n)
+ {
+ return new JSONNumber(n);
+ }
+ public static implicit operator int(JSONNode d)
+ {
+ return (d == null) ? 0 : d.AsInt;
+ }
+
+ public static implicit operator JSONNode(long n)
+ {
+ if (longAsString)
+ return new JSONString(n.ToString(CultureInfo.InvariantCulture));
+ return new JSONNumber(n);
+ }
+ public static implicit operator long(JSONNode d)
+ {
+ return (d == null) ? 0L : d.AsLong;
+ }
+
+ public static implicit operator JSONNode(ulong n)
+ {
+ if (longAsString)
+ return new JSONString(n.ToString(CultureInfo.InvariantCulture));
+ return new JSONNumber(n);
+ }
+ public static implicit operator ulong(JSONNode d)
+ {
+ return (d == null) ? 0 : d.AsULong;
+ }
+
+ public static implicit operator JSONNode(bool b)
+ {
+ return new JSONBool(b);
+ }
+ public static implicit operator bool(JSONNode d)
+ {
+ return (d == null) ? false : d.AsBool;
+ }
+
+ public static implicit operator JSONNode(KeyValuePair aKeyValue)
+ {
+ return aKeyValue.Value;
+ }
+
+ public static bool operator ==(JSONNode a, object b)
+ {
+ if (ReferenceEquals(a, b))
+ return true;
+ bool aIsNull = a is JSONNull || ReferenceEquals(a, null) || a is JSONLazyCreator;
+ bool bIsNull = b is JSONNull || ReferenceEquals(b, null) || b is JSONLazyCreator;
+ if (aIsNull && bIsNull)
+ return true;
+ return !aIsNull && a.Equals(b);
+ }
+
+ public static bool operator !=(JSONNode a, object b)
+ {
+ return !(a == b);
+ }
+
+ public override bool Equals(object obj)
+ {
+ return ReferenceEquals(this, obj);
+ }
+
+ public override int GetHashCode()
+ {
+ return base.GetHashCode();
+ }
+
+ #endregion operators
+
+ [ThreadStatic]
+ private static StringBuilder m_EscapeBuilder;
+ internal static StringBuilder EscapeBuilder
+ {
+ get
+ {
+ if (m_EscapeBuilder == null)
+ m_EscapeBuilder = new StringBuilder();
+ return m_EscapeBuilder;
+ }
+ }
+ internal static string Escape(string aText)
+ {
+ var sb = EscapeBuilder;
+ sb.Length = 0;
+ if (sb.Capacity < aText.Length + aText.Length / 10)
+ sb.Capacity = aText.Length + aText.Length / 10;
+ foreach (char c in aText)
+ {
+ switch (c)
+ {
+ case '\\':
+ sb.Append("\\\\");
+ break;
+ case '\"':
+ sb.Append("\\\"");
+ break;
+ case '\n':
+ sb.Append("\\n");
+ break;
+ case '\r':
+ sb.Append("\\r");
+ break;
+ case '\t':
+ sb.Append("\\t");
+ break;
+ case '\b':
+ sb.Append("\\b");
+ break;
+ case '\f':
+ sb.Append("\\f");
+ break;
+ default:
+ if (c < ' ' || (forceASCII && c > 127))
+ {
+ ushort val = c;
+ sb.Append("\\u").Append(val.ToString("X4"));
+ }
+ else
+ sb.Append(c);
+ break;
+ }
+ }
+ string result = sb.ToString();
+ sb.Length = 0;
+ return result;
+ }
+
+ private static JSONNode ParseElement(string token, bool quoted)
+ {
+ if (quoted)
+ return token;
+ if (token.Length <= 5)
+ {
+ string tmp = token.ToLower();
+ if (tmp == "false" || tmp == "true")
+ return tmp == "true";
+ if (tmp == "null")
+ return JSONNull.CreateOrGet();
+ }
+ double val;
+ if (double.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out val))
+ return val;
+ else
+ return token;
+ }
+
+ public static JSONNode Parse(string aJSON)
+ {
+ Stack stack = new Stack();
+ JSONNode ctx = null;
+ int i = 0;
+ StringBuilder Token = new StringBuilder();
+ string TokenName = "";
+ bool QuoteMode = false;
+ bool TokenIsQuoted = false;
+ bool HasNewlineChar = false;
+ while (i < aJSON.Length)
+ {
+ switch (aJSON[i])
+ {
+ case '{':
+ if (QuoteMode)
+ {
+ Token.Append(aJSON[i]);
+ break;
+ }
+ stack.Push(new JSONObject());
+ if (ctx != null)
+ {
+ ctx.Add(TokenName, stack.Peek());
+ }
+ TokenName = "";
+ Token.Length = 0;
+ ctx = stack.Peek();
+ HasNewlineChar = false;
+ break;
+
+ case '[':
+ if (QuoteMode)
+ {
+ Token.Append(aJSON[i]);
+ break;
+ }
+
+ stack.Push(new JSONArray());
+ if (ctx != null)
+ {
+ ctx.Add(TokenName, stack.Peek());
+ }
+ TokenName = "";
+ Token.Length = 0;
+ ctx = stack.Peek();
+ HasNewlineChar = false;
+ break;
+
+ case '}':
+ case ']':
+ if (QuoteMode)
+ {
+
+ Token.Append(aJSON[i]);
+ break;
+ }
+ if (stack.Count == 0)
+ throw new Exception("JSON Parse: Too many closing brackets");
+
+ stack.Pop();
+ if (Token.Length > 0 || TokenIsQuoted)
+ ctx.Add(TokenName, ParseElement(Token.ToString(), TokenIsQuoted));
+ if (ctx != null)
+ ctx.Inline = !HasNewlineChar;
+ TokenIsQuoted = false;
+ TokenName = "";
+ Token.Length = 0;
+ if (stack.Count > 0)
+ ctx = stack.Peek();
+ break;
+
+ case ':':
+ if (QuoteMode)
+ {
+ Token.Append(aJSON[i]);
+ break;
+ }
+ TokenName = Token.ToString();
+ Token.Length = 0;
+ TokenIsQuoted = false;
+ break;
+
+ case '"':
+ QuoteMode ^= true;
+ TokenIsQuoted |= QuoteMode;
+ break;
+
+ case ',':
+ if (QuoteMode)
+ {
+ Token.Append(aJSON[i]);
+ break;
+ }
+ if (Token.Length > 0 || TokenIsQuoted)
+ ctx.Add(TokenName, ParseElement(Token.ToString(), TokenIsQuoted));
+ TokenIsQuoted = false;
+ TokenName = "";
+ Token.Length = 0;
+ TokenIsQuoted = false;
+ break;
+
+ case '\r':
+ case '\n':
+ HasNewlineChar = true;
+ break;
+
+ case ' ':
+ case '\t':
+ if (QuoteMode)
+ Token.Append(aJSON[i]);
+ break;
+
+ case '\\':
+ ++i;
+ if (QuoteMode)
+ {
+ char C = aJSON[i];
+ switch (C)
+ {
+ case 't':
+ Token.Append('\t');
+ break;
+ case 'r':
+ Token.Append('\r');
+ break;
+ case 'n':
+ Token.Append('\n');
+ break;
+ case 'b':
+ Token.Append('\b');
+ break;
+ case 'f':
+ Token.Append('\f');
+ break;
+ case 'u':
+ {
+ string s = aJSON.Substring(i + 1, 4);
+ Token.Append((char)int.Parse(
+ s,
+ System.Globalization.NumberStyles.AllowHexSpecifier));
+ i += 4;
+ break;
+ }
+ default:
+ Token.Append(C);
+ break;
+ }
+ }
+ break;
+ case '/':
+ if (allowLineComments && !QuoteMode && i + 1 < aJSON.Length && aJSON[i + 1] == '/')
+ {
+ while (++i < aJSON.Length && aJSON[i] != '\n' && aJSON[i] != '\r') ;
+ break;
+ }
+ Token.Append(aJSON[i]);
+ break;
+ case '\uFEFF': // remove / ignore BOM (Byte Order Mark)
+ break;
+
+ default:
+ Token.Append(aJSON[i]);
+ break;
+ }
+ ++i;
+ }
+ if (QuoteMode)
+ {
+ throw new Exception("JSON Parse: Quotation marks seems to be messed up.");
+ }
+ if (ctx == null)
+ return ParseElement(Token.ToString(), TokenIsQuoted);
+ return ctx;
+ }
+
+ }
+ // End of JSONNode
+
+ internal partial class JSONArray : JSONNode
+ {
+ private List m_List = new List();
+ private bool inline = false;
+ public override bool Inline
+ {
+ get { return inline; }
+ set { inline = value; }
+ }
+
+ public override JSONNodeType Tag { get { return JSONNodeType.Array; } }
+ public override bool IsArray { get { return true; } }
+ public override Enumerator GetEnumerator() { return new Enumerator(m_List.GetEnumerator()); }
+
+ public override JSONNode this[int aIndex]
+ {
+ get
+ {
+ if (aIndex < 0 || aIndex >= m_List.Count)
+ return new JSONLazyCreator(this);
+ return m_List[aIndex];
+ }
+ set
+ {
+ if (value == null)
+ value = JSONNull.CreateOrGet();
+ if (aIndex < 0 || aIndex >= m_List.Count)
+ m_List.Add(value);
+ else
+ m_List[aIndex] = value;
+ }
+ }
+
+ public override JSONNode this[string aKey]
+ {
+ get { return new JSONLazyCreator(this); }
+ set
+ {
+ if (value == null)
+ value = JSONNull.CreateOrGet();
+ m_List.Add(value);
+ }
+ }
+
+ public override int Count
+ {
+ get { return m_List.Count; }
+ }
+
+ public override void Add(string aKey, JSONNode aItem)
+ {
+ if (aItem == null)
+ aItem = JSONNull.CreateOrGet();
+ m_List.Add(aItem);
+ }
+
+ public override JSONNode Remove(int aIndex)
+ {
+ if (aIndex < 0 || aIndex >= m_List.Count)
+ return null;
+ JSONNode tmp = m_List[aIndex];
+ m_List.RemoveAt(aIndex);
+ return tmp;
+ }
+
+ public override JSONNode Remove(JSONNode aNode)
+ {
+ m_List.Remove(aNode);
+ return aNode;
+ }
+
+ public override void Clear()
+ {
+ m_List.Clear();
+ }
+
+ public override JSONNode Clone()
+ {
+ var node = new JSONArray();
+ node.m_List.Capacity = m_List.Capacity;
+ foreach (var n in m_List)
+ {
+ if (n != null)
+ node.Add(n.Clone());
+ else
+ node.Add(null);
+ }
+ return node;
+ }
+
+ public override IEnumerable Children
+ {
+ get
+ {
+ foreach (JSONNode N in m_List)
+ yield return N;
+ }
+ }
+
+
+ internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode)
+ {
+ aSB.Append('[');
+ int count = m_List.Count;
+ if (inline)
+ aMode = JSONTextMode.Compact;
+ for (int i = 0; i < count; i++)
+ {
+ if (i > 0)
+ aSB.Append(',');
+ if (aMode == JSONTextMode.Indent)
+ aSB.AppendLine();
+
+ if (aMode == JSONTextMode.Indent)
+ aSB.Append(' ', aIndent + aIndentInc);
+ m_List[i].WriteToStringBuilder(aSB, aIndent + aIndentInc, aIndentInc, aMode);
+ }
+ if (aMode == JSONTextMode.Indent)
+ aSB.AppendLine().Append(' ', aIndent);
+ aSB.Append(']');
+ }
+ }
+ // End of JSONArray
+
+ internal partial class JSONObject : JSONNode
+ {
+ private Dictionary m_Dict = new Dictionary();
+
+ private bool inline = false;
+ public override bool Inline
+ {
+ get { return inline; }
+ set { inline = value; }
+ }
+
+ public override JSONNodeType Tag { get { return JSONNodeType.Object; } }
+ public override bool IsObject { get { return true; } }
+
+ public override Enumerator GetEnumerator() { return new Enumerator(m_Dict.GetEnumerator()); }
+
+
+ public override JSONNode this[string aKey]
+ {
+ get
+ {
+ if (m_Dict.ContainsKey(aKey))
+ return m_Dict[aKey];
+ else
+ return new JSONLazyCreator(this, aKey);
+ }
+ set
+ {
+ if (value == null)
+ value = JSONNull.CreateOrGet();
+ if (m_Dict.ContainsKey(aKey))
+ m_Dict[aKey] = value;
+ else
+ m_Dict.Add(aKey, value);
+ }
+ }
+
+ public override JSONNode this[int aIndex]
+ {
+ get
+ {
+ if (aIndex < 0 || aIndex >= m_Dict.Count)
+ return null;
+ return m_Dict.ElementAt(aIndex).Value;
+ }
+ set
+ {
+ if (value == null)
+ value = JSONNull.CreateOrGet();
+ if (aIndex < 0 || aIndex >= m_Dict.Count)
+ return;
+ string key = m_Dict.ElementAt(aIndex).Key;
+ m_Dict[key] = value;
+ }
+ }
+
+ public override int Count
+ {
+ get { return m_Dict.Count; }
+ }
+
+ public override void Add(string aKey, JSONNode aItem)
+ {
+ if (aItem == null)
+ aItem = JSONNull.CreateOrGet();
+
+ if (aKey != null)
+ {
+ if (m_Dict.ContainsKey(aKey))
+ m_Dict[aKey] = aItem;
+ else
+ m_Dict.Add(aKey, aItem);
+ }
+ else
+ m_Dict.Add(Guid.NewGuid().ToString(), aItem);
+ }
+
+ public override JSONNode Remove(string aKey)
+ {
+ if (!m_Dict.ContainsKey(aKey))
+ return null;
+ JSONNode tmp = m_Dict[aKey];
+ m_Dict.Remove(aKey);
+ return tmp;
+ }
+
+ public override JSONNode Remove(int aIndex)
+ {
+ if (aIndex < 0 || aIndex >= m_Dict.Count)
+ return null;
+ var item = m_Dict.ElementAt(aIndex);
+ m_Dict.Remove(item.Key);
+ return item.Value;
+ }
+
+ public override JSONNode Remove(JSONNode aNode)
+ {
+ try
+ {
+ var item = m_Dict.Where(k => k.Value == aNode).First();
+ m_Dict.Remove(item.Key);
+ return aNode;
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+ public override void Clear()
+ {
+ m_Dict.Clear();
+ }
+
+ public override JSONNode Clone()
+ {
+ var node = new JSONObject();
+ foreach (var n in m_Dict)
+ {
+ node.Add(n.Key, n.Value.Clone());
+ }
+ return node;
+ }
+
+ public override bool HasKey(string aKey)
+ {
+ return m_Dict.ContainsKey(aKey);
+ }
+
+ public override JSONNode GetValueOrDefault(string aKey, JSONNode aDefault)
+ {
+ JSONNode res;
+ if (m_Dict.TryGetValue(aKey, out res))
+ return res;
+ return aDefault;
+ }
+
+ public override IEnumerable Children
+ {
+ get
+ {
+ foreach (KeyValuePair N in m_Dict)
+ yield return N.Value;
+ }
+ }
+
+ internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode)
+ {
+ aSB.Append('{');
+ bool first = true;
+ if (inline)
+ aMode = JSONTextMode.Compact;
+ foreach (var k in m_Dict)
+ {
+ if (!first)
+ aSB.Append(',');
+ first = false;
+ if (aMode == JSONTextMode.Indent)
+ aSB.AppendLine();
+ if (aMode == JSONTextMode.Indent)
+ aSB.Append(' ', aIndent + aIndentInc);
+ aSB.Append('\"').Append(Escape(k.Key)).Append('\"');
+ if (aMode == JSONTextMode.Compact)
+ aSB.Append(':');
+ else
+ aSB.Append(": ");
+ k.Value.WriteToStringBuilder(aSB, aIndent + aIndentInc, aIndentInc, aMode);
+ }
+ if (aMode == JSONTextMode.Indent)
+ aSB.AppendLine().Append(' ', aIndent);
+ aSB.Append('}');
+ }
+
+ }
+ // End of JSONObject
+
+ internal partial class JSONString : JSONNode
+ {
+ private string m_Data;
+
+ public override JSONNodeType Tag { get { return JSONNodeType.String; } }
+ public override bool IsString { get { return true; } }
+
+ public override Enumerator GetEnumerator() { return new Enumerator(); }
+
+
+ public override string Value
+ {
+ get { return m_Data; }
+ set
+ {
+ m_Data = value;
+ }
+ }
+
+ public JSONString(string aData)
+ {
+ m_Data = aData;
+ }
+ public override JSONNode Clone()
+ {
+ return new JSONString(m_Data);
+ }
+
+ internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode)
+ {
+ aSB.Append('\"').Append(Escape(m_Data)).Append('\"');
+ }
+ public override bool Equals(object obj)
+ {
+ if (base.Equals(obj))
+ return true;
+ string s = obj as string;
+ if (s != null)
+ return m_Data == s;
+ JSONString s2 = obj as JSONString;
+ if (s2 != null)
+ return m_Data == s2.m_Data;
+ return false;
+ }
+ public override int GetHashCode()
+ {
+ return m_Data.GetHashCode();
+ }
+ public override void Clear()
+ {
+ m_Data = "";
+ }
+ }
+ // End of JSONString
+
+ internal partial class JSONNumber : JSONNode
+ {
+ private double m_Data;
+
+ public override JSONNodeType Tag { get { return JSONNodeType.Number; } }
+ public override bool IsNumber { get { return true; } }
+ public override Enumerator GetEnumerator() { return new Enumerator(); }
+
+ public override string Value
+ {
+ get { return m_Data.ToString(CultureInfo.InvariantCulture); }
+ set
+ {
+ double v;
+ if (double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out v))
+ m_Data = v;
+ }
+ }
+
+ public override double AsDouble
+ {
+ get { return m_Data; }
+ set { m_Data = value; }
+ }
+ public override long AsLong
+ {
+ get { return (long)m_Data; }
+ set { m_Data = value; }
+ }
+ public override ulong AsULong
+ {
+ get { return (ulong)m_Data; }
+ set { m_Data = value; }
+ }
+
+ public JSONNumber(double aData)
+ {
+ m_Data = aData;
+ }
+
+ public JSONNumber(string aData)
+ {
+ Value = aData;
+ }
+
+ public override JSONNode Clone()
+ {
+ return new JSONNumber(m_Data);
+ }
+
+ internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode)
+ {
+ aSB.Append(Value.ToString(CultureInfo.InvariantCulture));
+ }
+ private static bool IsNumeric(object value)
+ {
+ return value is int || value is uint
+ || value is float || value is double
+ || value is decimal
+ || value is long || value is ulong
+ || value is short || value is ushort
+ || value is sbyte || value is byte;
+ }
+ public override bool Equals(object obj)
+ {
+ if (obj == null)
+ return false;
+ if (base.Equals(obj))
+ return true;
+ JSONNumber s2 = obj as JSONNumber;
+ if (s2 != null)
+ return m_Data == s2.m_Data;
+ if (IsNumeric(obj))
+ return Convert.ToDouble(obj) == m_Data;
+ return false;
+ }
+ public override int GetHashCode()
+ {
+ return m_Data.GetHashCode();
+ }
+ public override void Clear()
+ {
+ m_Data = 0;
+ }
+ }
+ // End of JSONNumber
+
+ internal partial class JSONBool : JSONNode
+ {
+ private bool m_Data;
+
+ public override JSONNodeType Tag { get { return JSONNodeType.Boolean; } }
+ public override bool IsBoolean { get { return true; } }
+ public override Enumerator GetEnumerator() { return new Enumerator(); }
+
+ public override string Value
+ {
+ get { return m_Data.ToString(); }
+ set
+ {
+ bool v;
+ if (bool.TryParse(value, out v))
+ m_Data = v;
+ }
+ }
+ public override bool AsBool
+ {
+ get { return m_Data; }
+ set { m_Data = value; }
+ }
+
+ public JSONBool(bool aData)
+ {
+ m_Data = aData;
+ }
+
+ public JSONBool(string aData)
+ {
+ Value = aData;
+ }
+
+ public override JSONNode Clone()
+ {
+ return new JSONBool(m_Data);
+ }
+
+ internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode)
+ {
+ aSB.Append((m_Data) ? "true" : "false");
+ }
+ public override bool Equals(object obj)
+ {
+ if (obj == null)
+ return false;
+ if (obj is bool)
+ return m_Data == (bool)obj;
+ return false;
+ }
+ public override int GetHashCode()
+ {
+ return m_Data.GetHashCode();
+ }
+ public override void Clear()
+ {
+ m_Data = false;
+ }
+ }
+ // End of JSONBool
+
+ internal partial class JSONNull : JSONNode
+ {
+ static JSONNull m_StaticInstance = new JSONNull();
+ public static bool reuseSameInstance = true;
+ public static JSONNull CreateOrGet()
+ {
+ if (reuseSameInstance)
+ return m_StaticInstance;
+ return new JSONNull();
+ }
+ private JSONNull() { }
+
+ public override JSONNodeType Tag { get { return JSONNodeType.NullValue; } }
+ public override bool IsNull { get { return true; } }
+ public override Enumerator GetEnumerator() { return new Enumerator(); }
+
+ public override string Value
+ {
+ get { return "null"; }
+ set { }
+ }
+ public override bool AsBool
+ {
+ get { return false; }
+ set { }
+ }
+
+ public override JSONNode Clone()
+ {
+ return CreateOrGet();
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (object.ReferenceEquals(this, obj))
+ return true;
+ return (obj is JSONNull);
+ }
+ public override int GetHashCode()
+ {
+ return 0;
+ }
+
+ internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode)
+ {
+ aSB.Append("null");
+ }
+ }
+ // End of JSONNull
+
+ internal partial class JSONLazyCreator : JSONNode
+ {
+ private JSONNode m_Node = null;
+ private string m_Key = null;
+ public override JSONNodeType Tag { get { return JSONNodeType.None; } }
+ public override Enumerator GetEnumerator() { return new Enumerator(); }
+
+ public JSONLazyCreator(JSONNode aNode)
+ {
+ m_Node = aNode;
+ m_Key = null;
+ }
+
+ public JSONLazyCreator(JSONNode aNode, string aKey)
+ {
+ m_Node = aNode;
+ m_Key = aKey;
+ }
+
+ private T Set(T aVal) where T : JSONNode
+ {
+ if (m_Key == null)
+ m_Node.Add(aVal);
+ else
+ m_Node.Add(m_Key, aVal);
+ m_Node = null; // Be GC friendly.
+ return aVal;
+ }
+
+ public override JSONNode this[int aIndex]
+ {
+ get { return new JSONLazyCreator(this); }
+ set { Set(new JSONArray()).Add(value); }
+ }
+
+ public override JSONNode this[string aKey]
+ {
+ get { return new JSONLazyCreator(this, aKey); }
+ set { Set(new JSONObject()).Add(aKey, value); }
+ }
+
+ public override void Add(JSONNode aItem)
+ {
+ Set(new JSONArray()).Add(aItem);
+ }
+
+ public override void Add(string aKey, JSONNode aItem)
+ {
+ Set(new JSONObject()).Add(aKey, aItem);
+ }
+
+ public static bool operator ==(JSONLazyCreator a, object b)
+ {
+ if (b == null)
+ return true;
+ return System.Object.ReferenceEquals(a, b);
+ }
+
+ public static bool operator !=(JSONLazyCreator a, object b)
+ {
+ return !(a == b);
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj == null)
+ return true;
+ return System.Object.ReferenceEquals(this, obj);
+ }
+
+ public override int GetHashCode()
+ {
+ return 0;
+ }
+
+ public override int AsInt
+ {
+ get { Set(new JSONNumber(0)); return 0; }
+ set { Set(new JSONNumber(value)); }
+ }
+
+ public override float AsFloat
+ {
+ get { Set(new JSONNumber(0.0f)); return 0.0f; }
+ set { Set(new JSONNumber(value)); }
+ }
+
+ public override double AsDouble
+ {
+ get { Set(new JSONNumber(0.0)); return 0.0; }
+ set { Set(new JSONNumber(value)); }
+ }
+
+ public override long AsLong
+ {
+ get
+ {
+ if (longAsString)
+ Set(new JSONString("0"));
+ else
+ Set(new JSONNumber(0.0));
+ return 0L;
+ }
+ set
+ {
+ if (longAsString)
+ Set(new JSONString(value.ToString(CultureInfo.InvariantCulture)));
+ else
+ Set(new JSONNumber(value));
+ }
+ }
+
+ public override ulong AsULong
+ {
+ get
+ {
+ if (longAsString)
+ Set(new JSONString("0"));
+ else
+ Set(new JSONNumber(0.0));
+ return 0L;
+ }
+ set
+ {
+ if (longAsString)
+ Set(new JSONString(value.ToString(CultureInfo.InvariantCulture)));
+ else
+ Set(new JSONNumber(value));
+ }
+ }
+
+ public override bool AsBool
+ {
+ get { Set(new JSONBool(false)); return false; }
+ set { Set(new JSONBool(value)); }
+ }
+
+ public override JSONArray AsArray
+ {
+ get { return Set(new JSONArray()); }
+ }
+
+ public override JSONObject AsObject
+ {
+ get { return Set(new JSONObject()); }
+ }
+ internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode)
+ {
+ aSB.Append("null");
+ }
+ }
+ // End of JSONLazyCreator
+
+ internal static class JSON
+ {
+ public static JSONNode Parse(string aJSON)
+ {
+ return JSONNode.Parse(aJSON);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Editor/SimpleJSON.cs.meta b/Editor/SimpleJSON.cs.meta
new file mode 100644
index 0000000..ac83825
--- /dev/null
+++ b/Editor/SimpleJSON.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: ed8e7c93c781e48df93d063a3c0d2972
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Editor/VisualStudioCodeInstallation.cs b/Editor/VisualStudioCodeInstallation.cs
index 55c6102..7a1b5a5 100644
--- a/Editor/VisualStudioCodeInstallation.cs
+++ b/Editor/VisualStudioCodeInstallation.cs
@@ -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();
+ 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)
diff --git a/Editor/VisualStudioEditor.cs b/Editor/VisualStudioEditor.cs
index a956699..a4f93ea 100644
--- a/Editor/VisualStudioEditor.cs
+++ b/Editor/VisualStudioEditor.cs
@@ -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
diff --git a/Editor/VisualStudioForWindowsInstallation.cs b/Editor/VisualStudioForWindowsInstallation.cs
index c193c17..6138df2 100644
--- a/Editor/VisualStudioForWindowsInstallation.cs
+++ b/Editor/VisualStudioForWindowsInstallation.cs
@@ -228,7 +228,24 @@ namespace Microsoft.Unity.VisualStudio.Editor
if (string.IsNullOrWhiteSpace(progpath))
return Enumerable.Empty();
- 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 QueryVsWhere(string progpath, string arguments)
+ {
+ var result = ProcessRunner.StartAndWaitForExit(progpath, arguments);
if (!result.Success)
throw new Exception($"Failure while running vswhere: {result.Error}");
diff --git a/package.json b/package.json
index b6a3971..a7d9aaa 100644
--- a/package.json
+++ b/package.json
@@ -2,25 +2,25 @@
"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.",
- "version": "2.0.20",
+ "version": "2.0.21",
"unity": "2019.4",
"unityRelease": "25f1",
"dependencies": {
"com.unity.test-framework": "1.1.9"
},
"relatedPackages": {
- "com.unity.ide.visualstudio.tests": "2.0.20"
+ "com.unity.ide.visualstudio.tests": "2.0.21"
},
"_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": {
- "footprint": "7d769a8558c7768417b16fc2ac8477cf69234049"
+ "footprint": "33ecd838a074080a82176ca86b21c837ba396a28"
},
"documentationUrl": "https://docs.unity3d.com/Packages/com.unity.ide.visualstudio@2.0/manual/index.html",
"repository": {
"url": "https://github.cds.internal.unity3d.com/unity/com.unity.ide.visualstudio.git",
"type": "git",
- "revision": "b7bf23d23806ac75645bfa12acadcfc11468a383"
+ "revision": "eb2b500b99f6429d3d9dcb8acbcbd366dfbb42be"
}
}