diff --git a/src/TextMateSharp.Grammars/GrammarDefinition.cs b/src/TextMateSharp.Grammars/GrammarDefinition.cs index 9f4004a..11a36ce 100644 --- a/src/TextMateSharp.Grammars/GrammarDefinition.cs +++ b/src/TextMateSharp.Grammars/GrammarDefinition.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.IO; -using System.Text.Json; -using System.Text.Json.Serialization; + +using SimpleJSON; using TextMateSharp.Grammars.Resources; @@ -9,30 +9,21 @@ namespace TextMateSharp.Grammars { public class Engines { - [JsonPropertyName("engines")] public string VsCode { get; set; } } public class Scripts { - [JsonPropertyName("update-grammar")] public string UpdateGrammar { get; set; } } public class Language { - [JsonPropertyName("id")] public string Id { get; set; } - [JsonPropertyName("extensions")] public List Extensions { get; set; } - [JsonPropertyName("aliases")] public List Aliases { get; set; } - [JsonPropertyName("configuration")] public string ConfigurationFile { get; set; } public LanguageConfiguration Configuration {get; set;} - - // May be null - [JsonPropertyName("mimetypes")] public List MimeTypes { get; set; } public override string ToString() @@ -46,62 +37,188 @@ public override string ToString() public class Grammar { - [JsonPropertyName("language")] public string Language { get; set; } - [JsonPropertyName("scopeName")] public string ScopeName { get; set; } - [JsonPropertyName("path")] public string Path { get; set; } } public class Snippet { - [JsonPropertyName("language")] public string Language { get; set; } - [JsonPropertyName("path")] public string Path { get; set; } } public class Contributes { - [JsonPropertyName("languages")] public List Languages { get; set; } - [JsonPropertyName("grammars")] public List Grammars { get; set; } - [JsonPropertyName("snippets")] public List Snippets { get; set; } } public class Repository { - [JsonPropertyName("type")] public string Type { get; set; } - [JsonPropertyName("url")] public string Url { get; set; } } public class GrammarDefinition { - [JsonPropertyName("name")] public string Name { get; set; } - [JsonPropertyName("displayName")] public string DisplayName { get; set; } - [JsonPropertyName("description")] public string Description { get; set; } - [JsonPropertyName("version")] public string Version { get; set; } - [JsonPropertyName("publisher")] public string Publisher { get; set; } - [JsonPropertyName("license")] public string License { get; set; } - [JsonPropertyName("engines")] public Engines Engines { get; set; } - [JsonPropertyName("scripts")] public Scripts Scripts { get; set; } - [JsonPropertyName("contributes")] public Contributes Contributes { get; set; } - [JsonPropertyName("repository")] public Repository Repository { get; set; } public LanguageSnippets LanguageSnippets { get; set; } + + public static GrammarDefinition Parse(Stream stream) + { + using (StreamReader reader = new StreamReader(stream)) + { + return Parse(reader.ReadToEnd()); + } + } + + public static GrammarDefinition Parse(string jsonContent) + { + JSONNode json = JSON.Parse(jsonContent); + if (json == null) + return null; + + var definition = new GrammarDefinition + { + Name = json["name"], + DisplayName = json["displayName"], + Description = json["description"], + Version = json["version"], + Publisher = json["publisher"], + License = json["license"] + }; + + if (json["engines"] != null && !json["engines"].IsNull) + { + definition.Engines = new Engines + { + VsCode = json["engines"]["vscode"] + }; + } + + if (json["scripts"] != null && !json["scripts"].IsNull) + { + definition.Scripts = new Scripts + { + UpdateGrammar = json["scripts"]["update-grammar"] + }; + } + + if (json["repository"] != null && !json["repository"].IsNull) + { + definition.Repository = new Repository + { + Type = json["repository"]["type"], + Url = json["repository"]["url"] + }; + } + + if (json["contributes"] != null && !json["contributes"].IsNull) + { + definition.Contributes = ParseContributes(json["contributes"]); + } + + return definition; + } + + private static Contributes ParseContributes(JSONNode node) + { + var contributes = new Contributes(); + + if (node["languages"] != null && node["languages"].IsArray) + { + contributes.Languages = new List(); + foreach (JSONNode langNode in node["languages"].Children) + { + var language = new Language + { + Id = langNode["id"], + ConfigurationFile = GetNullableString(langNode["configuration"]) + }; + + if (langNode["extensions"] != null && langNode["extensions"].IsArray) + { + language.Extensions = new List(); + foreach (JSONNode ext in langNode["extensions"].Children) + { + language.Extensions.Add(ext.Value); + } + } + + if (langNode["aliases"] != null && langNode["aliases"].IsArray) + { + language.Aliases = new List(); + foreach (JSONNode alias in langNode["aliases"].Children) + { + language.Aliases.Add(alias.Value); + } + } + + if (langNode["mimetypes"] != null && langNode["mimetypes"].IsArray) + { + language.MimeTypes = new List(); + foreach (JSONNode mime in langNode["mimetypes"].Children) + { + language.MimeTypes.Add(mime.Value); + } + } + + contributes.Languages.Add(language); + } + } + + if (node["grammars"] != null && node["grammars"].IsArray) + { + contributes.Grammars = new List(); + foreach (JSONNode grammarNode in node["grammars"].Children) + { + contributes.Grammars.Add(new Grammar + { + Language = grammarNode["language"], + ScopeName = grammarNode["scopeName"], + Path = grammarNode["path"] + }); + } + } + + if (node["snippets"] != null && node["snippets"].IsArray) + { + contributes.Snippets = new List(); + foreach (JSONNode snippetNode in node["snippets"].Children) + { + contributes.Snippets.Add(new Snippet + { + Language = snippetNode["language"], + Path = snippetNode["path"] + }); + } + } + + return contributes; + } + + /// + /// Helper to convert SimpleJSON string values to null when empty. + /// SimpleJSON returns "" for missing keys, but we need null for proper semantics. + /// + private static string GetNullableString(JSONNode node) + { + if (node == null || node.IsNull) + return null; + + string value = node.Value; + return string.IsNullOrEmpty(value) ? null : value; + } } } diff --git a/src/TextMateSharp.Grammars/JsonSerializationContext.cs b/src/TextMateSharp.Grammars/JsonSerializationContext.cs deleted file mode 100644 index 44ff696..0000000 --- a/src/TextMateSharp.Grammars/JsonSerializationContext.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace TextMateSharp.Grammars -{ - [JsonSerializable(typeof(GrammarDefinition))] - [JsonSerializable(typeof(LanguageSnippets))] - [JsonSerializable(typeof(LanguageSnippet))] - [JsonSerializable(typeof(LanguageConfiguration))] - [JsonSerializable(typeof(EnterRule))] - [JsonSerializable(typeof(AutoPair))] - [JsonSerializable(typeof(IList))] - internal sealed partial class JsonSerializationContext : JsonSerializerContext - { - } -} \ No newline at end of file diff --git a/src/TextMateSharp.Grammars/LanguageConfiguration.cs b/src/TextMateSharp.Grammars/LanguageConfiguration.cs index 655c99c..834d3a1 100644 --- a/src/TextMateSharp.Grammars/LanguageConfiguration.cs +++ b/src/TextMateSharp.Grammars/LanguageConfiguration.cs @@ -2,11 +2,10 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text.Json; -using System.Text.Json.Serialization; -using System.Threading; + +using SimpleJSON; + using TextMateSharp.Grammars.Resources; -using TextMateSharp.Internal.Grammars; using CharacterPair = System.Collections.Generic.IList; using StringPair = System.Collections.Generic.IList; @@ -15,36 +14,22 @@ namespace TextMateSharp.Grammars { public class LanguageConfiguration { - [JsonPropertyName("autoCloseBefore")] public string AutoCloseBefore { get; set; } - [JsonPropertyName("folding")] public Folding Folding { get; set; } - [JsonPropertyName("brackets")] public StringPair[] Brackets { get; set; } - [JsonPropertyName("comments")] public Comments Comments { get; set; } - [JsonPropertyName("autoClosingPairs")] - [JsonConverter(typeof(ClosingPairJsonConverter))] public AutoClosingPairs AutoClosingPairs { get; set; } - [JsonPropertyName("surroundingPairs")] - [JsonConverter(typeof(SurroundingPairJsonConverter))] public CharacterPair[] SurroundingPairs { get; set; } - [JsonPropertyName("indentationRules")] - [JsonConverter(typeof(IntentationRulesJsonConverter))] public Indentation IndentationRules { get; set; } - [JsonPropertyName("onEnterRules")] - [JsonConverter(typeof(EnterRulesJsonConverter))] public EnterRules EnterRules { get; set; } - private readonly static JsonSerializationContext jsonContext = new(new JsonSerializerOptions { AllowTrailingCommas = true, ReadCommentHandling = JsonCommentHandling.Skip }); - public static LanguageConfiguration Load(string grammarName, string configurationFile) { if (string.IsNullOrEmpty(configurationFile)) @@ -57,7 +42,7 @@ public static LanguageConfiguration Load(string grammarName, string configuratio using (StreamReader reader = new StreamReader(stream)) { - return JsonSerializer.Deserialize(stream, jsonContext.LanguageConfiguration); + return Parse(reader.ReadToEnd()); } } } @@ -70,41 +55,290 @@ public static LanguageConfiguration LoadFromLocal(string configurationFile) return null; } using (var fileStream = fileInfo.OpenRead()) + using (var reader = new StreamReader(fileStream)) + { + return Parse(reader.ReadToEnd()); + } + } + + public static LanguageConfiguration Parse(string jsonContent) + { + JSONNode json = JSON.Parse(jsonContent); + if (json == null) + return null; + + var config = new LanguageConfiguration + { + AutoCloseBefore = json["autoCloseBefore"] + }; + + // Parse folding + if (json["folding"] != null && !json["folding"].IsNull) + { + config.Folding = ParseFolding(json["folding"]); + } + + // Parse brackets + if (json["brackets"] != null && json["brackets"].IsArray) + { + config.Brackets = ParseBrackets(json["brackets"]); + } + + // Parse comments + if (json["comments"] != null && !json["comments"].IsNull) + { + config.Comments = ParseComments(json["comments"]); + } + + // Parse autoClosingPairs + if (json["autoClosingPairs"] != null && json["autoClosingPairs"].IsArray) + { + config.AutoClosingPairs = ParseAutoClosingPairs(json["autoClosingPairs"]); + } + + // Parse surroundingPairs + if (json["surroundingPairs"] != null && json["surroundingPairs"].IsArray) + { + config.SurroundingPairs = ParseSurroundingPairs(json["surroundingPairs"]); + } + + // Parse indentationRules + if (json["indentationRules"] != null && !json["indentationRules"].IsNull) + { + config.IndentationRules = ParseIndentation(json["indentationRules"]); + } + + // Parse onEnterRules + if (json["onEnterRules"] != null && json["onEnterRules"].IsArray) + { + config.EnterRules = ParseEnterRules(json["onEnterRules"]); + } + + return config; + } + + private static Folding ParseFolding(JSONNode node) + { + var folding = new Folding + { + OffSide = node["offSide"].AsBool + }; + + if (node["markers"] != null && !node["markers"].IsNull) + { + folding.Markers = new Markers + { + Start = node["markers"]["start"], + End = node["markers"]["end"] + }; + } + + return folding; + } + + private static StringPair[] ParseBrackets(JSONNode node) + { + var brackets = new List(); + foreach (JSONNode bracketPair in node.Children) + { + if (bracketPair.IsArray && bracketPair.Count >= 2) + { + brackets.Add(new List { bracketPair[0].Value, bracketPair[1].Value }); + } + } + return brackets.ToArray(); + } + + private static Comments ParseComments(JSONNode node) + { + var comments = new Comments + { + LineComment = node["lineComment"] + }; + + if (node["blockComment"] != null && node["blockComment"].IsArray && node["blockComment"].Count >= 2) + { + comments.BlockComment = new List + { + node["blockComment"][0].Value, + node["blockComment"][1].Value + }; + } + + return comments; + } + + private static AutoClosingPairs ParseAutoClosingPairs(JSONNode node) + { + var autoClosingPairs = new AutoClosingPairs(); + var charPairs = new List(); + var autoPairs = new List(); + + foreach (JSONNode pairNode in node.Children) { - return JsonSerializer.Deserialize(fileStream, jsonContext.LanguageConfiguration); + if (pairNode.IsArray) + { + // It's a simple character pair like ["(", ")"] + var pair = new List(); + foreach (JSONNode charNode in pairNode.Children) + { + string value = charNode.Value; + if (!string.IsNullOrEmpty(value)) + { + pair.Add(value[0]); + } + } + if (pair.Count > 0) + { + charPairs.Add(pair); + } + } + else if (pairNode.IsObject) + { + // It's an object like {"open": "(", "close": ")", "notIn": ["string"]} + var autoPair = new AutoPair + { + Open = pairNode["open"], + Close = pairNode["close"] + }; + + if (pairNode["notIn"] != null && pairNode["notIn"].IsArray) + { + var notIn = new List(); + foreach (JSONNode notInNode in pairNode["notIn"].Children) + { + notIn.Add(notInNode.Value); + } + autoPair.NotIn = notIn; + } + + autoPairs.Add(autoPair); + } } + + autoClosingPairs.CharPairs = charPairs.ToArray(); + autoClosingPairs.AutoPairs = autoPairs.ToArray(); + + return autoClosingPairs; + } + + private static CharacterPair[] ParseSurroundingPairs(JSONNode node) + { + var surroundingPairs = new List(); + + foreach (JSONNode pairNode in node.Children) + { + if (pairNode.IsArray) + { + var pair = new List(); + foreach (JSONNode charNode in pairNode.Children) + { + string value = charNode.Value; + if (!string.IsNullOrEmpty(value)) + { + pair.Add(value[0]); + } + } + if (pair.Count > 0) + { + surroundingPairs.Add(pair.ToArray()); + } + } + else if (pairNode.IsObject) + { + string open = pairNode["open"]; + string close = pairNode["close"]; + if (!string.IsNullOrEmpty(open) && !string.IsNullOrEmpty(close)) + { + surroundingPairs.Add(new char[] { open[0], close[0] }); + } + } + } + + return surroundingPairs.ToArray(); + } + + private static Indentation ParseIndentation(JSONNode node) + { + var indentation = new Indentation(); + + // Handle both string format and object format with pattern property + indentation.Increase = GetPatternValue(node["increaseIndentPattern"]); + indentation.Decrease = GetPatternValue(node["decreaseIndentPattern"]); + indentation.Unindent = GetPatternValue(node["unIndentedLinePattern"]); + + return indentation; + } + + private static string GetPatternValue(JSONNode node) + { + if (node == null || node.IsNull) + return string.Empty; + + if (node.IsString) + return node.Value; + + if (node.IsObject && node["pattern"] != null) + return node["pattern"].Value; + + return string.Empty; + } + + private static EnterRules ParseEnterRules(JSONNode node) + { + var enterRules = new EnterRules(); + + foreach (JSONNode ruleNode in node.Children) + { + if (ruleNode.IsObject) + { + enterRules.Rules.Add(ParseEnterRule(ruleNode)); + } + } + + return enterRules; + } + + private static EnterRule ParseEnterRule(JSONNode node) + { + var rule = new EnterRule(); + + // beforeText can be a string or an object with pattern + rule.BeforeText = GetPatternValue(node["beforeText"]); + rule.AfterText = GetPatternValue(node["afterText"]); + + // action is an object with indent/appendText + if (node["action"] != null && !node["action"].IsNull) + { + rule.ActionIndent = node["action"]["indent"]; + rule.AppendText = node["action"]["appendText"]; + } + + return rule; } - } - + public class Region { - [JsonPropertyName("prefix")] public string Prefix { get; set; } - [JsonPropertyName("body")] public string[] Body { get; set; } - [JsonPropertyName("description")] public string Description { get; set; } } public class Markers { - [JsonPropertyName("start")] public string Start { get; set; } - [JsonPropertyName("end")] public string End { get; set; } } public class Folding { - [JsonPropertyName("offSide")] public bool OffSide { get; set; } - [JsonPropertyName("markers")] public Markers Markers { get; set; } public bool IsEmpty @@ -118,10 +352,8 @@ public bool IsEmpty public class Comments { - [JsonPropertyName("lineComment")] public string LineComment { get; set; } - [JsonPropertyName("blockComment")] public StringPair BlockComment { get; set; } } @@ -142,7 +374,6 @@ public bool IsEmpty } } - [JsonConverter(typeof(EnterRuleJsonConverter))] public class EnterRule { public string BeforeText { get; set; } @@ -175,7 +406,6 @@ public class AutoClosingPairs public AutoPair[] AutoPairs { get; set; } = new AutoPair[] { }; } - [JsonConverter(typeof(LanguageSnippetJsonConverter))] public class LanguageSnippet { public string Prefix { get; set; } @@ -185,19 +415,15 @@ public class LanguageSnippet public string Description { get; set; } } - [JsonConverter(typeof(LanguageSnippetsJsonConverter))] public class LanguageSnippets { public IDictionary Snippets { get; set; } = new Dictionary(); - private readonly static JsonSerializationContext jsonContext = new(new JsonSerializerOptions { AllowTrailingCommas = true, ReadCommentHandling = JsonCommentHandling.Skip }); public static LanguageSnippets Load(string grammarName, Contributes contributes) { if (contributes == null || contributes.Snippets == null) return null; - var result = new LanguageSnippets(); - foreach (var snippet in contributes.Snippets) { using (Stream stream = ResourceLoader.TryOpenLanguageSnippet(grammarName, snippet.Path)) @@ -207,442 +433,65 @@ public static LanguageSnippets Load(string grammarName, Contributes contributes) using (StreamReader reader = new StreamReader(stream)) { - return JsonSerializer.Deserialize(stream, jsonContext.LanguageSnippets); + return Parse(reader.ReadToEnd()); } } } - return result; + return new LanguageSnippets(); } public static LanguageSnippets LoadFromLocal(string filePath) { - var fileInfo = new FileInfo(filePath); + var fileInfo = new FileInfo(filePath); if (!fileInfo.Exists) { return null; } using (var fileStream = fileInfo.OpenRead()) + using (var reader = new StreamReader(fileStream)) { - return JsonSerializer.Deserialize(fileStream, jsonContext.LanguageSnippets); + return Parse(reader.ReadToEnd()); } } - } - public class ClosingPairJsonConverter : JsonConverter - { - public override AutoClosingPairs Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public static LanguageSnippets Parse(string jsonContent) { - var autoClosingPairs = new AutoClosingPairs(); - - if (reader.TokenType == JsonTokenType.StartArray) - { - var charPairs = new List(); - var autoPairs = new List(); - - while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) - { - switch (reader.TokenType) - { - case JsonTokenType.StartArray: - var charPair = new List(); - while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) - { - switch (reader.TokenType) - { - case JsonTokenType.String: - charPair.Add(reader.GetString().ToCharArray().First()); - break; - } - } - //var charPair = JsonSerializer.Deserialize(ref reader, CharacterPairSerializationContext.Default.CharPair); - charPairs.Add(charPair); - break; - - case JsonTokenType.StartObject: - var autoPair = new AutoPair(); - string propName = string.Empty; - while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) - { - switch (reader.TokenType) - { - case JsonTokenType.StartArray: - if (string.Compare(propName, "notIn") == 0) - { - autoPair.NotIn = JsonSerializer.Deserialize(ref reader, JsonSerializationContext.Default.IListString); - } - break; - case JsonTokenType.PropertyName: - propName = reader.GetString(); - break; - case JsonTokenType.String: - switch (propName) - { - case "open": - autoPair.Open = reader.GetString(); - break; - case "close": - autoPair.Close = reader.GetString(); - break; - } - break; - } - } - autoPairs.Add(autoPair); - break; - } - } - - autoClosingPairs.CharPairs = charPairs.ToArray(); - autoClosingPairs.AutoPairs = autoPairs.ToArray(); - } - - return autoClosingPairs; - } + JSONNode json = JSON.Parse(jsonContent); + if (json == null) + return null; - public override void Write(Utf8JsonWriter writer, AutoClosingPairs value, JsonSerializerOptions options) - { - } - } + var snippets = new LanguageSnippets(); - public class SurroundingPairJsonConverter : JsonConverter - { - public override CharacterPair[] Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - var surroundingPairs = new List(); - if (reader.TokenType == JsonTokenType.StartArray) + foreach (var kvp in json.Linq) { - while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) + var snippet = new LanguageSnippet { - switch (reader.TokenType) - { - case JsonTokenType.StartArray: - var pair = new List(); - while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) - { - switch (reader.TokenType) - { - case JsonTokenType.String: - pair.Add(reader.GetString().First()); - break; - } - } - surroundingPairs.Add(pair.ToArray()); - break; - - case JsonTokenType.StartObject: - string propName = string.Empty; - string open = null; - string close = null; - while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) - { - switch (reader.TokenType) - { - case JsonTokenType.PropertyName: - propName = reader.GetString(); - break; - case JsonTokenType.String: - switch (propName) - { - case "open": - open = reader.GetString(); - break; - case "close": - close = reader.GetString(); - break; - } - break; - } - } - - if (open != null && close != null) - surroundingPairs.Add(new char[] { open.First(), close.First() }); - - break; - } - } - } - - return surroundingPairs.ToArray(); - } + Prefix = kvp.Value["prefix"], + Description = kvp.Value["description"] + }; - public override void Write(Utf8JsonWriter writer, CharacterPair[] value, JsonSerializerOptions options) - { - } - } - - public class IntentationRulesJsonConverter : JsonConverter - { - public override Indentation Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - var rules = new Indentation(); - - if (reader.TokenType == JsonTokenType.StartObject) - { - string propName = string.Empty; - string internalName = string.Empty; - while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) + if (kvp.Value["body"] != null) { - switch (reader.TokenType) + if (kvp.Value["body"].IsArray) { - case JsonTokenType.PropertyName: - propName = reader.GetString(); - break; - case JsonTokenType.String: - switch (propName) - { - case "increaseIndentPattern": - rules.Increase = reader.GetString(); - break; - case "decreaseIndentPattern": - rules.Decrease = reader.GetString(); - break; - case "unIndentedLinePattern": - rules.Unindent = reader.GetString(); - break; - } - - break; - - case JsonTokenType.StartObject: - while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) - { - switch (reader.TokenType) - { - case JsonTokenType.PropertyName: - internalName = reader.GetString(); - break; - case JsonTokenType.String: - switch (internalName) - { - case "pattern": - switch (propName) - { - case "increaseIndentPattern": - rules.Increase = reader.GetString(); - break; - case "decreaseIndentPattern": - rules.Decrease = reader.GetString(); - break; - case "unIndentedLinePattern": - rules.Unindent = reader.GetString(); - break; - } - - break; - } - - break; - } - } - - break; + var bodyList = new List(); + foreach (JSONNode bodyNode in kvp.Value["body"].Children) + { + bodyList.Add(bodyNode.Value); + } + snippet.Body = bodyList.ToArray(); } - } - } - - return rules; - } - - public override void Write(Utf8JsonWriter writer, Indentation value, JsonSerializerOptions options) - { - } - } - - public class EnterRulesJsonConverter : JsonConverter - { - public override EnterRules Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - var enterRules = new EnterRules(); - - if (reader.TokenType == JsonTokenType.StartArray) - { - while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) - { - switch (reader.TokenType) + else if (kvp.Value["body"].IsString) { - case JsonTokenType.StartArray: - break; - - case JsonTokenType.StartObject: - EnterRule rule = JsonSerializer.Deserialize(ref reader, JsonSerializationContext.Default.EnterRule); - enterRules.Rules.Add(rule); - break; + snippet.Body = new string[] { kvp.Value["body"].Value }; } } - } - - return enterRules; - } - - public override void Write(Utf8JsonWriter writer, EnterRules value, JsonSerializerOptions options) - { - } - } - - public class EnterRuleJsonConverter : JsonConverter - { - public override EnterRule Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - var enterRule = new EnterRule(); - string propName = string.Empty; - string internalName = string.Empty; - if (reader.TokenType == JsonTokenType.StartObject) - { - while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) - { - switch (reader.TokenType) - { - case JsonTokenType.PropertyName: - propName = reader.GetString(); - break; - case JsonTokenType.StartObject: - while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) - { - switch (reader.TokenType) - { - case JsonTokenType.PropertyName: - internalName = reader.GetString(); - break; - case JsonTokenType.String: - switch (internalName) - { - case "pattern": - switch (propName) - { - case "beforeText": - enterRule.BeforeText = reader.GetString(); - break; - case "afterText": - enterRule.AfterText = reader.GetString(); - break; - } - - break; - case "indent": - enterRule.ActionIndent = reader.GetString(); - break; - case "appendText": - enterRule.AppendText = reader.GetString(); - break; - } - - break; - } - } - - break; - case JsonTokenType.String: - switch (propName) - { - case "beforeText": - enterRule.BeforeText = reader.GetString(); - break; - case "afterText": - enterRule.AfterText = reader.GetString(); - break; - } - - break; - } - } - } - - return enterRule; - } - - public override void Write(Utf8JsonWriter writer, EnterRule value, JsonSerializerOptions options) - { - } - } - - public class LanguageSnippetsJsonConverter : JsonConverter - { - public override LanguageSnippets Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - var snippets = new LanguageSnippets(); - if (reader.TokenType == JsonTokenType.StartObject) - { - string propName = string.Empty; - while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) - { - switch (reader.TokenType) - { - case JsonTokenType.PropertyName: - propName = reader.GetString(); - break; - case JsonTokenType.StartObject: - LanguageSnippet snippet = JsonSerializer.Deserialize(ref reader, JsonSerializationContext.Default.LanguageSnippet); - snippets.Snippets.Add(propName, snippet); - break; - } - } + snippets.Snippets.Add(kvp.Key, snippet); } return snippets; } - - public override void Write(Utf8JsonWriter writer, LanguageSnippets value, JsonSerializerOptions options) - { - } - } - - public class LanguageSnippetJsonConverter : JsonConverter - { - public override LanguageSnippet Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - var snippet = new LanguageSnippet(); - - if (reader.TokenType == JsonTokenType.StartObject) - { - string propName = string.Empty; - while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) - { - switch (reader.TokenType) - { - case JsonTokenType.PropertyName: - propName = reader.GetString(); - break; - case JsonTokenType.StartArray: - IList body = new List(); - while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) - { - if (string.Compare(propName, "body") == 0) - { - switch (reader.TokenType) - { - case JsonTokenType.String: - body.Add(reader.GetString()); - break; - } - } - } - - snippet.Body = body.ToArray(); - - break; - case JsonTokenType.String: - switch (propName) - { - case "prefix": - snippet.Prefix = reader.GetString(); - break; - case "description": - snippet.Description = reader.GetString(); - break; - } - - break; - } - } - } - - return snippet; - } - - public override void Write(Utf8JsonWriter writer, LanguageSnippet value, JsonSerializerOptions options) - { - } } -} \ No newline at end of file +} diff --git a/src/TextMateSharp.Grammars/RegistryOptions.cs b/src/TextMateSharp.Grammars/RegistryOptions.cs index b0967be..a339f55 100644 --- a/src/TextMateSharp.Grammars/RegistryOptions.cs +++ b/src/TextMateSharp.Grammars/RegistryOptions.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text.Json; + using TextMateSharp.Grammars.Resources; using TextMateSharp.Internal.Grammars.Reader; using TextMateSharp.Internal.Themes.Reader; @@ -73,8 +73,7 @@ public void LoadFromLocalFile(string grammarName, FileInfo packageJsonFileInfo, var baseDir = packageJsonFileInfo.Directory?.FullName ?? string.Empty; using (Stream stream = packageJsonFileInfo.OpenRead()) { - var definition = JsonSerializer.Deserialize( - stream, JsonSerializationContext.Default.GrammarDefinition); + var definition = GrammarDefinition.Parse(stream); if (definition == null) { return; @@ -85,7 +84,7 @@ public void LoadFromLocalFile(string grammarName, FileInfo packageJsonFileInfo, { foreach (var language in contributes.Languages) { - if (language.ConfigurationFile == null) + if (string.IsNullOrEmpty(language.ConfigurationFile)) { language.Configuration = null; continue; @@ -283,9 +282,7 @@ void InitializeAvailableGrammars() { using (Stream stream = ResourceLoader.OpenGrammarPackage(grammar)) { - GrammarDefinition definition = JsonSerializer.Deserialize( - stream, - JsonSerializationContext.Default.GrammarDefinition); + GrammarDefinition definition = GrammarDefinition.Parse(stream); foreach (var language in definition.Contributes.Languages) { diff --git a/src/TextMateSharp.Grammars/TextMateSharp.Grammars.csproj b/src/TextMateSharp.Grammars/TextMateSharp.Grammars.csproj index 51dd68d..d954796 100644 --- a/src/TextMateSharp.Grammars/TextMateSharp.Grammars.csproj +++ b/src/TextMateSharp.Grammars/TextMateSharp.Grammars.csproj @@ -19,9 +19,6 @@ - - - diff --git a/src/TextMateSharp/Internal/Parser/Json/JSONPListParser.cs b/src/TextMateSharp/Internal/Parser/Json/JSONPListParser.cs index bfab5dd..b475c70 100644 --- a/src/TextMateSharp/Internal/Parser/Json/JSONPListParser.cs +++ b/src/TextMateSharp/Internal/Parser/Json/JSONPListParser.cs @@ -1,6 +1,6 @@ -using System; using System.IO; -using System.Text.Json; + +using SimpleJSON; namespace TextMateSharp.Internal.Parser.Json { @@ -17,85 +17,59 @@ public T Parse(StreamReader contents) { PList pList = new PList(theme); - var buffer = new byte[2048]; - - JsonReaderOptions options = new JsonReaderOptions() - { - AllowTrailingCommas = true, - CommentHandling = JsonCommentHandling.Skip - }; - - int size = contents.BaseStream.Read(buffer, 0, buffer.Length); - var reader = new Utf8JsonReader(buffer.AsSpan(0, size), isFinalBlock: false, state: new JsonReaderState(options)); - - while (true) - { - int bytesRead = -1; - - while (!reader.Read()) - { - bytesRead = GetMoreBytesFromStream(contents.BaseStream, ref buffer, ref reader); - - if (bytesRead == 0) - break; - } + string jsonContent = contents.ReadToEnd(); + JSONNode root = JSON.Parse(jsonContent); - if (bytesRead == 0) - break; + ProcessNode(root, pList); - var nextToken = reader.TokenType; - switch (nextToken) - { - case JsonTokenType.StartArray: - pList.StartElement("array"); - break; - case JsonTokenType.EndArray: - pList.EndElement("array"); - break; - case JsonTokenType.StartObject: - pList.StartElement("dict"); - break; - case JsonTokenType.EndObject: - pList.EndElement("dict"); - break; - case JsonTokenType.PropertyName: - pList.StartElement("key"); - pList.AddString(reader.GetString()); - pList.EndElement("key"); - break; - case JsonTokenType.String: - pList.StartElement("string"); - pList.AddString(reader.GetString()); - pList.EndElement("string"); - break; - } - } return pList.GetResult(); } - private static int GetMoreBytesFromStream(Stream stream, ref byte[] buffer, ref Utf8JsonReader reader) + private void ProcessNode(JSONNode node, PList pList) { - int bytesRead; - if (reader.BytesConsumed < buffer.Length) - { - ReadOnlySpan leftover = buffer.AsSpan((int)reader.BytesConsumed); + if (node == null) + return; - if (leftover.Length == buffer.Length) + if (node.IsArray) + { + pList.StartElement("array"); + foreach (JSONNode child in node.Children) { - Array.Resize(ref buffer, buffer.Length * 2); + ProcessNode(child, pList); } + pList.EndElement("array"); + } + else if (node.IsObject) + { + pList.StartElement("dict"); + foreach (var kvp in node.Linq) + { + pList.StartElement("key"); + pList.AddString(kvp.Key); + pList.EndElement("key"); - leftover.CopyTo(buffer); - bytesRead = stream.Read(buffer, leftover.Length, buffer.Length - leftover.Length); - reader = new Utf8JsonReader(buffer.AsSpan(0, bytesRead + leftover.Length), isFinalBlock: bytesRead == 0, reader.CurrentState); + ProcessNode(kvp.Value, pList); + } + pList.EndElement("dict"); } - else + else if (node.IsString) { - bytesRead = stream.Read(buffer, 0, buffer.Length); - reader = new Utf8JsonReader(buffer.AsSpan(0, bytesRead), isFinalBlock: bytesRead == 0, reader.CurrentState); + pList.StartElement("string"); + pList.AddString(node.Value); + pList.EndElement("string"); + } + else if (node.IsNumber) + { + pList.StartElement("string"); + pList.AddString(node.Value); + pList.EndElement("string"); + } + else if (node.IsBoolean) + { + pList.StartElement("string"); + pList.AddString(node.Value); + pList.EndElement("string"); } - - return bytesRead; } } } \ No newline at end of file diff --git a/src/TextMateSharp/Internal/Parser/Json/SimpleJSON.cs b/src/TextMateSharp/Internal/Parser/Json/SimpleJSON.cs new file mode 100644 index 0000000..9703d75 --- /dev/null +++ b/src/TextMateSharp/Internal/Parser/Json/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 +{ + public enum JSONNodeType + { + Array = 1, + Object = 2, + String = 3, + Number = 4, + NullValue = 5, + Boolean = 6, + None = 7, + Custom = 0xFF, + } + public enum JSONTextMode + { + Compact, + Indent + } + + public abstract partial class JSONNode + { + #region Enumerators + public 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; + } + } + public 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; } + } + public 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; } + } + + public 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 + + public 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 + + public 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.TryGetValue(aKey, out JSONNode outJsonNode)) + return outJsonNode; + 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 + + public 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 + + public 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 + + public 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 + + public 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 + + public static class JSON + { + public static JSONNode Parse(string aJSON) + { + return JSONNode.Parse(aJSON); + } + } +} diff --git a/src/TextMateSharp/TextMateSharp.csproj b/src/TextMateSharp/TextMateSharp.csproj index ecf76d5..10abcdd 100644 --- a/src/TextMateSharp/TextMateSharp.csproj +++ b/src/TextMateSharp/TextMateSharp.csproj @@ -20,7 +20,6 @@ TextMateSharp uses a wrapper around Oniguruma regex engine. -