/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/Xshd/V1Loader.cs

http://github.com/icsharpcode/ILSpy · C# · 340 lines · 284 code · 30 blank · 26 comment · 69 complexity · 309d0d2e9ffa8ec7732a1f7030bfb38b MD5 · raw file

  1. // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy of this
  4. // software and associated documentation files (the "Software"), to deal in the Software
  5. // without restriction, including without limitation the rights to use, copy, modify, merge,
  6. // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
  7. // to whom the Software is furnished to do so, subject to the following conditions:
  8. //
  9. // The above copyright notice and this permission notice shall be included in all copies or
  10. // substantial portions of the Software.
  11. //
  12. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  13. // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  14. // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
  15. // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  16. // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  17. // DEALINGS IN THE SOFTWARE.
  18. using System;
  19. using System.Globalization;
  20. using System.Text;
  21. using System.Text.RegularExpressions;
  22. using System.Windows;
  23. using System.Windows.Media;
  24. using System.Xml;
  25. using System.Xml.Schema;
  26. using ICSharpCode.AvalonEdit.Utils;
  27. namespace ICSharpCode.AvalonEdit.Highlighting.Xshd
  28. {
  29. /// <summary>
  30. /// Loads .xshd files, version 1.0.
  31. /// </summary>
  32. sealed class V1Loader
  33. {
  34. static XmlSchemaSet schemaSet;
  35. static XmlSchemaSet SchemaSet {
  36. get {
  37. if (schemaSet == null) {
  38. schemaSet = HighlightingLoader.LoadSchemaSet(new XmlTextReader(
  39. Resources.OpenStream("ModeV1.xsd")));
  40. }
  41. return schemaSet;
  42. }
  43. }
  44. public static XshdSyntaxDefinition LoadDefinition(XmlReader reader, bool skipValidation)
  45. {
  46. reader = HighlightingLoader.GetValidatingReader(reader, false, skipValidation ? null : SchemaSet);
  47. XmlDocument document = new XmlDocument();
  48. document.Load(reader);
  49. V1Loader loader = new V1Loader();
  50. return loader.ParseDefinition(document.DocumentElement);
  51. }
  52. XshdSyntaxDefinition ParseDefinition(XmlElement syntaxDefinition)
  53. {
  54. XshdSyntaxDefinition def = new XshdSyntaxDefinition();
  55. def.Name = syntaxDefinition.GetAttributeOrNull("name");
  56. if (syntaxDefinition.HasAttribute("extensions")) {
  57. def.Extensions.AddRange(syntaxDefinition.GetAttribute("extensions").Split(';', '|'));
  58. }
  59. XshdRuleSet mainRuleSetElement = null;
  60. foreach (XmlElement element in syntaxDefinition.GetElementsByTagName("RuleSet")) {
  61. XshdRuleSet ruleSet = ImportRuleSet(element);
  62. def.Elements.Add(ruleSet);
  63. if (ruleSet.Name == null)
  64. mainRuleSetElement = ruleSet;
  65. if (syntaxDefinition["Digits"] != null) {
  66. // create digit highlighting rule
  67. const string optionalExponent = @"([eE][+-]?[0-9]+)?";
  68. const string floatingPoint = @"\.[0-9]+";
  69. ruleSet.Elements.Add(
  70. new XshdRule {
  71. ColorReference = GetColorReference(syntaxDefinition["Digits"]),
  72. RegexType = XshdRegexType.IgnorePatternWhitespace,
  73. Regex = @"\b0[xX][0-9a-fA-F]+"
  74. + @"|"
  75. + @"(\b\d+(" + floatingPoint + ")?"
  76. + @"|" + floatingPoint + ")"
  77. + optionalExponent
  78. });
  79. }
  80. }
  81. if (syntaxDefinition.HasAttribute("extends") && mainRuleSetElement != null) {
  82. // convert 'extends="HTML"' to '<Import ruleSet="HTML/" />' in main rule set.
  83. mainRuleSetElement.Elements.Add(
  84. new XshdImport { RuleSetReference = new XshdReference<XshdRuleSet>(
  85. syntaxDefinition.GetAttribute("extends"), string.Empty
  86. ) });
  87. }
  88. return def;
  89. }
  90. static XshdColor GetColorFromElement(XmlElement element)
  91. {
  92. if (!element.HasAttribute("bold") && !element.HasAttribute("italic") && !element.HasAttribute("color") && !element.HasAttribute("bgcolor"))
  93. return null;
  94. XshdColor color = new XshdColor();
  95. if (element.HasAttribute("bold"))
  96. color.FontWeight = XmlConvert.ToBoolean(element.GetAttribute("bold")) ? FontWeights.Bold : FontWeights.Normal;
  97. if (element.HasAttribute("italic"))
  98. color.FontStyle = XmlConvert.ToBoolean(element.GetAttribute("italic")) ? FontStyles.Italic : FontStyles.Normal;
  99. if (element.HasAttribute("color"))
  100. color.Foreground = ParseColor(element.GetAttribute("color"));
  101. if (element.HasAttribute("bgcolor"))
  102. color.Background = ParseColor(element.GetAttribute("bgcolor"));
  103. return color;
  104. }
  105. static XshdReference<XshdColor> GetColorReference(XmlElement element)
  106. {
  107. XshdColor color = GetColorFromElement(element);
  108. if (color != null)
  109. return new XshdReference<XshdColor>(color);
  110. else
  111. return new XshdReference<XshdColor>();
  112. }
  113. static HighlightingBrush ParseColor(string c)
  114. {
  115. if (c.StartsWith("#", StringComparison.Ordinal)) {
  116. int a = 255;
  117. int offset = 0;
  118. if (c.Length > 7) {
  119. offset = 2;
  120. a = Int32.Parse(c.Substring(1,2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
  121. }
  122. int r = Int32.Parse(c.Substring(1 + offset,2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
  123. int g = Int32.Parse(c.Substring(3 + offset,2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
  124. int b = Int32.Parse(c.Substring(5 + offset,2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
  125. return new SimpleHighlightingBrush(Color.FromArgb((byte)a, (byte)r, (byte)g, (byte)b));
  126. } else if (c.StartsWith("SystemColors.", StringComparison.Ordinal)) {
  127. return V2Loader.GetSystemColorBrush(null, c);
  128. } else {
  129. return new SimpleHighlightingBrush((Color)V2Loader.ColorConverter.ConvertFromInvariantString(c));
  130. }
  131. }
  132. char ruleSetEscapeCharacter;
  133. XshdRuleSet ImportRuleSet(XmlElement element)
  134. {
  135. XshdRuleSet ruleSet = new XshdRuleSet();
  136. ruleSet.Name = element.GetAttributeOrNull("name");
  137. if (element.HasAttribute("escapecharacter")) {
  138. ruleSetEscapeCharacter = element.GetAttribute("escapecharacter")[0];
  139. } else {
  140. ruleSetEscapeCharacter = '\0';
  141. }
  142. if (element.HasAttribute("reference")) {
  143. ruleSet.Elements.Add(
  144. new XshdImport { RuleSetReference = new XshdReference<XshdRuleSet>(
  145. element.GetAttribute("reference"), string.Empty
  146. ) });
  147. }
  148. ruleSet.IgnoreCase = element.GetBoolAttribute("ignorecase");
  149. foreach (XmlElement el in element.GetElementsByTagName("KeyWords")) {
  150. XshdKeywords keywords = new XshdKeywords();
  151. keywords.ColorReference = GetColorReference(el);
  152. // we have to handle old syntax highlighting definitions that contain
  153. // empty keywords or empty keyword groups
  154. foreach (XmlElement node in el.GetElementsByTagName("Key")) {
  155. string word = node.GetAttribute("word");
  156. if (!string.IsNullOrEmpty(word))
  157. keywords.Words.Add(word);
  158. }
  159. if (keywords.Words.Count > 0) {
  160. ruleSet.Elements.Add(keywords);
  161. }
  162. }
  163. foreach (XmlElement el in element.GetElementsByTagName("Span")) {
  164. ruleSet.Elements.Add(ImportSpan(el));
  165. }
  166. foreach (XmlElement el in element.GetElementsByTagName("MarkPrevious")) {
  167. ruleSet.Elements.Add(ImportMarkPrevNext(el, false));
  168. }
  169. foreach (XmlElement el in element.GetElementsByTagName("MarkFollowing")) {
  170. ruleSet.Elements.Add(ImportMarkPrevNext(el, true));
  171. }
  172. return ruleSet;
  173. }
  174. static XshdRule ImportMarkPrevNext(XmlElement el, bool markFollowing)
  175. {
  176. bool markMarker = el.GetBoolAttribute("markmarker") ?? false;
  177. string what = Regex.Escape(el.InnerText);
  178. const string identifier = @"[\d\w_]+";
  179. const string whitespace = @"\s*";
  180. string regex;
  181. if (markFollowing) {
  182. if (markMarker) {
  183. regex = what + whitespace + identifier;
  184. } else {
  185. regex = "(?<=(" + what + whitespace + "))" + identifier;
  186. }
  187. } else {
  188. if (markMarker) {
  189. regex = identifier + whitespace + what;
  190. } else {
  191. regex = identifier + "(?=(" + whitespace + what + "))";
  192. }
  193. }
  194. return new XshdRule {
  195. ColorReference = GetColorReference(el),
  196. Regex = regex,
  197. RegexType = XshdRegexType.IgnorePatternWhitespace
  198. };
  199. }
  200. XshdSpan ImportSpan(XmlElement element)
  201. {
  202. XshdSpan span = new XshdSpan();
  203. if (element.HasAttribute("rule")) {
  204. span.RuleSetReference = new XshdReference<XshdRuleSet>(null, element.GetAttribute("rule"));
  205. }
  206. char escapeCharacter = ruleSetEscapeCharacter;
  207. if (element.HasAttribute("escapecharacter")) {
  208. escapeCharacter = element.GetAttribute("escapecharacter")[0];
  209. }
  210. span.Multiline = !(element.GetBoolAttribute("stopateol") ?? false);
  211. span.SpanColorReference = GetColorReference(element);
  212. span.BeginRegexType = XshdRegexType.IgnorePatternWhitespace;
  213. span.BeginRegex = ImportRegex(element["Begin"].InnerText,
  214. element["Begin"].GetBoolAttribute("singleword") ?? false,
  215. element["Begin"].GetBoolAttribute("startofline"));
  216. span.BeginColorReference = GetColorReference(element["Begin"]);
  217. string endElementText = string.Empty;
  218. if (element["End"] != null) {
  219. span.EndRegexType = XshdRegexType.IgnorePatternWhitespace;
  220. endElementText = element["End"].InnerText;
  221. span.EndRegex = ImportRegex(endElementText,
  222. element["End"].GetBoolAttribute("singleword") ?? false,
  223. null);
  224. span.EndColorReference = GetColorReference(element["End"]);
  225. }
  226. if (escapeCharacter != '\0') {
  227. XshdRuleSet ruleSet = new XshdRuleSet();
  228. if (endElementText.Length == 1 && endElementText[0] == escapeCharacter) {
  229. // ""-style escape
  230. ruleSet.Elements.Add(new XshdSpan {
  231. BeginRegex = Regex.Escape(endElementText + endElementText),
  232. EndRegex = ""
  233. });
  234. } else {
  235. // \"-style escape
  236. ruleSet.Elements.Add(new XshdSpan {
  237. BeginRegex = Regex.Escape(escapeCharacter.ToString()),
  238. EndRegex = "."
  239. });
  240. }
  241. if (span.RuleSetReference.ReferencedElement != null) {
  242. ruleSet.Elements.Add(new XshdImport { RuleSetReference = span.RuleSetReference });
  243. }
  244. span.RuleSetReference = new XshdReference<XshdRuleSet>(ruleSet);
  245. }
  246. return span;
  247. }
  248. static string ImportRegex(string expr, bool singleWord, bool? startOfLine)
  249. {
  250. StringBuilder b = new StringBuilder();
  251. if (startOfLine != null) {
  252. if (startOfLine.Value) {
  253. b.Append(@"(?<=(^\s*))");
  254. } else {
  255. b.Append(@"(?<!(^\s*))");
  256. }
  257. } else {
  258. if (singleWord)
  259. b.Append(@"\b");
  260. }
  261. for (int i = 0; i < expr.Length; i++) {
  262. char c = expr[i];
  263. if (c == '@') {
  264. ++i;
  265. if (i == expr.Length)
  266. throw new HighlightingDefinitionInvalidException("Unexpected end of @ sequence, use @@ to look for a single @.");
  267. switch (expr[i]) {
  268. case 'C': // match whitespace or punctuation
  269. b.Append(@"[^\w\d_]");
  270. break;
  271. case '!': // negative lookahead
  272. {
  273. StringBuilder whatmatch = new StringBuilder();
  274. ++i;
  275. while (i < expr.Length && expr[i] != '@') {
  276. whatmatch.Append(expr[i++]);
  277. }
  278. b.Append("(?!(");
  279. b.Append(Regex.Escape(whatmatch.ToString()));
  280. b.Append("))");
  281. }
  282. break;
  283. case '-': // negative lookbehind
  284. {
  285. StringBuilder whatmatch = new StringBuilder();
  286. ++i;
  287. while (i < expr.Length && expr[i] != '@') {
  288. whatmatch.Append(expr[i++]);
  289. }
  290. b.Append("(?<!(");
  291. b.Append(Regex.Escape(whatmatch.ToString()));
  292. b.Append("))");
  293. }
  294. break;
  295. case '@':
  296. b.Append("@");
  297. break;
  298. default:
  299. throw new HighlightingDefinitionInvalidException("Unknown character in @ sequence.");
  300. }
  301. } else {
  302. b.Append(Regex.Escape(c.ToString()));
  303. }
  304. }
  305. if (singleWord)
  306. b.Append(@"\b");
  307. return b.ToString();
  308. }
  309. }
  310. }