// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team // // 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.Globalization; using System.Text; using System.Text.RegularExpressions; using System.Windows; using System.Windows.Media; using System.Xml; using System.Xml.Schema; using ICSharpCode.AvalonEdit.Utils; namespace ICSharpCode.AvalonEdit.Highlighting.Xshd { /// /// Loads .xshd files, version 1.0. /// sealed class V1Loader { static XmlSchemaSet schemaSet; static XmlSchemaSet SchemaSet { get { if (schemaSet == null) { schemaSet = HighlightingLoader.LoadSchemaSet(new XmlTextReader( Resources.OpenStream("ModeV1.xsd"))); } return schemaSet; } } public static XshdSyntaxDefinition LoadDefinition(XmlReader reader, bool skipValidation) { reader = HighlightingLoader.GetValidatingReader(reader, false, skipValidation ? null : SchemaSet); XmlDocument document = new XmlDocument(); document.Load(reader); V1Loader loader = new V1Loader(); return loader.ParseDefinition(document.DocumentElement); } XshdSyntaxDefinition ParseDefinition(XmlElement syntaxDefinition) { XshdSyntaxDefinition def = new XshdSyntaxDefinition(); def.Name = syntaxDefinition.GetAttributeOrNull("name"); if (syntaxDefinition.HasAttribute("extensions")) { def.Extensions.AddRange(syntaxDefinition.GetAttribute("extensions").Split(';', '|')); } XshdRuleSet mainRuleSetElement = null; foreach (XmlElement element in syntaxDefinition.GetElementsByTagName("RuleSet")) { XshdRuleSet ruleSet = ImportRuleSet(element); def.Elements.Add(ruleSet); if (ruleSet.Name == null) mainRuleSetElement = ruleSet; if (syntaxDefinition["Digits"] != null) { // create digit highlighting rule const string optionalExponent = @"([eE][+-]?[0-9]+)?"; const string floatingPoint = @"\.[0-9]+"; ruleSet.Elements.Add( new XshdRule { ColorReference = GetColorReference(syntaxDefinition["Digits"]), RegexType = XshdRegexType.IgnorePatternWhitespace, Regex = @"\b0[xX][0-9a-fA-F]+" + @"|" + @"(\b\d+(" + floatingPoint + ")?" + @"|" + floatingPoint + ")" + optionalExponent }); } } if (syntaxDefinition.HasAttribute("extends") && mainRuleSetElement != null) { // convert 'extends="HTML"' to '' in main rule set. mainRuleSetElement.Elements.Add( new XshdImport { RuleSetReference = new XshdReference( syntaxDefinition.GetAttribute("extends"), string.Empty ) }); } return def; } static XshdColor GetColorFromElement(XmlElement element) { if (!element.HasAttribute("bold") && !element.HasAttribute("italic") && !element.HasAttribute("color") && !element.HasAttribute("bgcolor")) return null; XshdColor color = new XshdColor(); if (element.HasAttribute("bold")) color.FontWeight = XmlConvert.ToBoolean(element.GetAttribute("bold")) ? FontWeights.Bold : FontWeights.Normal; if (element.HasAttribute("italic")) color.FontStyle = XmlConvert.ToBoolean(element.GetAttribute("italic")) ? FontStyles.Italic : FontStyles.Normal; if (element.HasAttribute("color")) color.Foreground = ParseColor(element.GetAttribute("color")); if (element.HasAttribute("bgcolor")) color.Background = ParseColor(element.GetAttribute("bgcolor")); return color; } static XshdReference GetColorReference(XmlElement element) { XshdColor color = GetColorFromElement(element); if (color != null) return new XshdReference(color); else return new XshdReference(); } static HighlightingBrush ParseColor(string c) { if (c.StartsWith("#", StringComparison.Ordinal)) { int a = 255; int offset = 0; if (c.Length > 7) { offset = 2; a = Int32.Parse(c.Substring(1,2), NumberStyles.HexNumber, CultureInfo.InvariantCulture); } int r = Int32.Parse(c.Substring(1 + offset,2), NumberStyles.HexNumber, CultureInfo.InvariantCulture); int g = Int32.Parse(c.Substring(3 + offset,2), NumberStyles.HexNumber, CultureInfo.InvariantCulture); int b = Int32.Parse(c.Substring(5 + offset,2), NumberStyles.HexNumber, CultureInfo.InvariantCulture); return new SimpleHighlightingBrush(Color.FromArgb((byte)a, (byte)r, (byte)g, (byte)b)); } else if (c.StartsWith("SystemColors.", StringComparison.Ordinal)) { return V2Loader.GetSystemColorBrush(null, c); } else { return new SimpleHighlightingBrush((Color)V2Loader.ColorConverter.ConvertFromInvariantString(c)); } } char ruleSetEscapeCharacter; XshdRuleSet ImportRuleSet(XmlElement element) { XshdRuleSet ruleSet = new XshdRuleSet(); ruleSet.Name = element.GetAttributeOrNull("name"); if (element.HasAttribute("escapecharacter")) { ruleSetEscapeCharacter = element.GetAttribute("escapecharacter")[0]; } else { ruleSetEscapeCharacter = '\0'; } if (element.HasAttribute("reference")) { ruleSet.Elements.Add( new XshdImport { RuleSetReference = new XshdReference( element.GetAttribute("reference"), string.Empty ) }); } ruleSet.IgnoreCase = element.GetBoolAttribute("ignorecase"); foreach (XmlElement el in element.GetElementsByTagName("KeyWords")) { XshdKeywords keywords = new XshdKeywords(); keywords.ColorReference = GetColorReference(el); // we have to handle old syntax highlighting definitions that contain // empty keywords or empty keyword groups foreach (XmlElement node in el.GetElementsByTagName("Key")) { string word = node.GetAttribute("word"); if (!string.IsNullOrEmpty(word)) keywords.Words.Add(word); } if (keywords.Words.Count > 0) { ruleSet.Elements.Add(keywords); } } foreach (XmlElement el in element.GetElementsByTagName("Span")) { ruleSet.Elements.Add(ImportSpan(el)); } foreach (XmlElement el in element.GetElementsByTagName("MarkPrevious")) { ruleSet.Elements.Add(ImportMarkPrevNext(el, false)); } foreach (XmlElement el in element.GetElementsByTagName("MarkFollowing")) { ruleSet.Elements.Add(ImportMarkPrevNext(el, true)); } return ruleSet; } static XshdRule ImportMarkPrevNext(XmlElement el, bool markFollowing) { bool markMarker = el.GetBoolAttribute("markmarker") ?? false; string what = Regex.Escape(el.InnerText); const string identifier = @"[\d\w_]+"; const string whitespace = @"\s*"; string regex; if (markFollowing) { if (markMarker) { regex = what + whitespace + identifier; } else { regex = "(?<=(" + what + whitespace + "))" + identifier; } } else { if (markMarker) { regex = identifier + whitespace + what; } else { regex = identifier + "(?=(" + whitespace + what + "))"; } } return new XshdRule { ColorReference = GetColorReference(el), Regex = regex, RegexType = XshdRegexType.IgnorePatternWhitespace }; } XshdSpan ImportSpan(XmlElement element) { XshdSpan span = new XshdSpan(); if (element.HasAttribute("rule")) { span.RuleSetReference = new XshdReference(null, element.GetAttribute("rule")); } char escapeCharacter = ruleSetEscapeCharacter; if (element.HasAttribute("escapecharacter")) { escapeCharacter = element.GetAttribute("escapecharacter")[0]; } span.Multiline = !(element.GetBoolAttribute("stopateol") ?? false); span.SpanColorReference = GetColorReference(element); span.BeginRegexType = XshdRegexType.IgnorePatternWhitespace; span.BeginRegex = ImportRegex(element["Begin"].InnerText, element["Begin"].GetBoolAttribute("singleword") ?? false, element["Begin"].GetBoolAttribute("startofline")); span.BeginColorReference = GetColorReference(element["Begin"]); string endElementText = string.Empty; if (element["End"] != null) { span.EndRegexType = XshdRegexType.IgnorePatternWhitespace; endElementText = element["End"].InnerText; span.EndRegex = ImportRegex(endElementText, element["End"].GetBoolAttribute("singleword") ?? false, null); span.EndColorReference = GetColorReference(element["End"]); } if (escapeCharacter != '\0') { XshdRuleSet ruleSet = new XshdRuleSet(); if (endElementText.Length == 1 && endElementText[0] == escapeCharacter) { // ""-style escape ruleSet.Elements.Add(new XshdSpan { BeginRegex = Regex.Escape(endElementText + endElementText), EndRegex = "" }); } else { // \"-style escape ruleSet.Elements.Add(new XshdSpan { BeginRegex = Regex.Escape(escapeCharacter.ToString()), EndRegex = "." }); } if (span.RuleSetReference.ReferencedElement != null) { ruleSet.Elements.Add(new XshdImport { RuleSetReference = span.RuleSetReference }); } span.RuleSetReference = new XshdReference(ruleSet); } return span; } static string ImportRegex(string expr, bool singleWord, bool? startOfLine) { StringBuilder b = new StringBuilder(); if (startOfLine != null) { if (startOfLine.Value) { b.Append(@"(?<=(^\s*))"); } else { b.Append(@"(?