PageRenderTime 27ms CodeModel.GetById 10ms app.highlight 11ms RepoModel.GetById 1ms app.codeStats 1ms

/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
 19using System;
 20using System.Globalization;
 21using System.Text;
 22using System.Text.RegularExpressions;
 23using System.Windows;
 24using System.Windows.Media;
 25using System.Xml;
 26using System.Xml.Schema;
 27
 28using ICSharpCode.AvalonEdit.Utils;
 29
 30namespace ICSharpCode.AvalonEdit.Highlighting.Xshd
 31{
 32	/// <summary>
 33	/// Loads .xshd files, version 1.0.
 34	/// </summary>
 35	sealed class V1Loader
 36	{
 37		static XmlSchemaSet schemaSet;
 38		
 39		static XmlSchemaSet SchemaSet {
 40			get {
 41				if (schemaSet == null) {
 42					schemaSet = HighlightingLoader.LoadSchemaSet(new XmlTextReader(
 43						Resources.OpenStream("ModeV1.xsd")));
 44				}
 45				return schemaSet;
 46			}
 47		}
 48		
 49		public static XshdSyntaxDefinition LoadDefinition(XmlReader reader, bool skipValidation)
 50		{
 51			reader = HighlightingLoader.GetValidatingReader(reader, false, skipValidation ? null : SchemaSet);
 52			XmlDocument document = new XmlDocument();
 53			document.Load(reader);
 54			V1Loader loader = new V1Loader();
 55			return loader.ParseDefinition(document.DocumentElement);
 56		}
 57		
 58		XshdSyntaxDefinition ParseDefinition(XmlElement syntaxDefinition)
 59		{
 60			XshdSyntaxDefinition def = new XshdSyntaxDefinition();
 61			def.Name = syntaxDefinition.GetAttributeOrNull("name");
 62			if (syntaxDefinition.HasAttribute("extensions")) {
 63				def.Extensions.AddRange(syntaxDefinition.GetAttribute("extensions").Split(';', '|'));
 64			}
 65			
 66			XshdRuleSet mainRuleSetElement = null;
 67			foreach (XmlElement element in syntaxDefinition.GetElementsByTagName("RuleSet")) {
 68				XshdRuleSet ruleSet = ImportRuleSet(element);
 69				def.Elements.Add(ruleSet);
 70				if (ruleSet.Name == null)
 71					mainRuleSetElement = ruleSet;
 72				
 73				if (syntaxDefinition["Digits"] != null) {
 74					// create digit highlighting rule
 75					
 76					const string optionalExponent = @"([eE][+-]?[0-9]+)?";
 77					const string floatingPoint = @"\.[0-9]+";
 78					ruleSet.Elements.Add(
 79						new XshdRule {
 80							ColorReference = GetColorReference(syntaxDefinition["Digits"]),
 81							RegexType = XshdRegexType.IgnorePatternWhitespace,
 82							Regex = @"\b0[xX][0-9a-fA-F]+"
 83								+ @"|"
 84								+ @"(\b\d+(" + floatingPoint + ")?"
 85								+ @"|" + floatingPoint + ")"
 86								+ optionalExponent
 87						});
 88				}
 89			}
 90			
 91			if (syntaxDefinition.HasAttribute("extends") && mainRuleSetElement != null) {
 92				// convert 'extends="HTML"' to '<Import ruleSet="HTML/" />' in main rule set.
 93				mainRuleSetElement.Elements.Add(
 94					new XshdImport { RuleSetReference = new XshdReference<XshdRuleSet>(
 95						syntaxDefinition.GetAttribute("extends"), string.Empty
 96					) });
 97			}
 98			return def;
 99		}
100		
101		static XshdColor GetColorFromElement(XmlElement element)
102		{
103			if (!element.HasAttribute("bold") && !element.HasAttribute("italic") && !element.HasAttribute("color") && !element.HasAttribute("bgcolor"))
104				return null;
105			XshdColor color = new XshdColor();
106			if (element.HasAttribute("bold"))
107				color.FontWeight = XmlConvert.ToBoolean(element.GetAttribute("bold")) ? FontWeights.Bold : FontWeights.Normal;
108			if (element.HasAttribute("italic"))
109				color.FontStyle = XmlConvert.ToBoolean(element.GetAttribute("italic")) ? FontStyles.Italic : FontStyles.Normal;
110			if (element.HasAttribute("color"))
111				color.Foreground = ParseColor(element.GetAttribute("color"));
112			if (element.HasAttribute("bgcolor"))
113				color.Background = ParseColor(element.GetAttribute("bgcolor"));
114			return color;
115		}
116		
117		static XshdReference<XshdColor> GetColorReference(XmlElement element)
118		{
119			XshdColor color = GetColorFromElement(element);
120			if (color != null)
121				return new XshdReference<XshdColor>(color);
122			else
123				return new XshdReference<XshdColor>();
124		}
125		
126		static HighlightingBrush ParseColor(string c)
127		{
128			if (c.StartsWith("#", StringComparison.Ordinal)) {
129				int a = 255;
130				int offset = 0;
131				if (c.Length > 7) {
132					offset = 2;
133					a = Int32.Parse(c.Substring(1,2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
134				}
135				
136				int r = Int32.Parse(c.Substring(1 + offset,2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
137				int g = Int32.Parse(c.Substring(3 + offset,2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
138				int b = Int32.Parse(c.Substring(5 + offset,2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
139				return new SimpleHighlightingBrush(Color.FromArgb((byte)a, (byte)r, (byte)g, (byte)b));
140			} else if (c.StartsWith("SystemColors.", StringComparison.Ordinal)) {
141				return V2Loader.GetSystemColorBrush(null, c);
142			} else {
143				return new SimpleHighlightingBrush((Color)V2Loader.ColorConverter.ConvertFromInvariantString(c));
144			}
145		}
146		
147		char ruleSetEscapeCharacter;
148		
149		XshdRuleSet ImportRuleSet(XmlElement element)
150		{
151			XshdRuleSet ruleSet = new XshdRuleSet();
152			ruleSet.Name = element.GetAttributeOrNull("name");
153			
154			if (element.HasAttribute("escapecharacter")) {
155				ruleSetEscapeCharacter = element.GetAttribute("escapecharacter")[0];
156			} else {
157				ruleSetEscapeCharacter = '\0';
158			}
159			
160			if (element.HasAttribute("reference")) {
161				ruleSet.Elements.Add(
162					new XshdImport { RuleSetReference = new XshdReference<XshdRuleSet>(
163						element.GetAttribute("reference"), string.Empty
164					) });
165			}
166			ruleSet.IgnoreCase = element.GetBoolAttribute("ignorecase");
167			
168			foreach (XmlElement el in element.GetElementsByTagName("KeyWords")) {
169				XshdKeywords keywords = new XshdKeywords();
170				keywords.ColorReference = GetColorReference(el);
171				// we have to handle old syntax highlighting definitions that contain
172				// empty keywords or empty keyword groups
173				foreach (XmlElement node in el.GetElementsByTagName("Key")) {
174					string word = node.GetAttribute("word");
175					if (!string.IsNullOrEmpty(word))
176						keywords.Words.Add(word);
177				}
178				if (keywords.Words.Count > 0) {
179					ruleSet.Elements.Add(keywords);
180				}
181			}
182			
183			foreach (XmlElement el in element.GetElementsByTagName("Span")) {
184				ruleSet.Elements.Add(ImportSpan(el));
185			}
186			
187			foreach (XmlElement el in element.GetElementsByTagName("MarkPrevious")) {
188				ruleSet.Elements.Add(ImportMarkPrevNext(el, false));
189			}
190			foreach (XmlElement el in element.GetElementsByTagName("MarkFollowing")) {
191				ruleSet.Elements.Add(ImportMarkPrevNext(el, true));
192			}
193			
194			return ruleSet;
195		}
196		
197		static XshdRule ImportMarkPrevNext(XmlElement el, bool markFollowing)
198		{
199			bool markMarker = el.GetBoolAttribute("markmarker") ?? false;
200			string what = Regex.Escape(el.InnerText);
201			const string identifier = @"[\d\w_]+";
202			const string whitespace = @"\s*";
203			
204			string regex;
205			if (markFollowing) {
206				if (markMarker) {
207					regex = what + whitespace + identifier;
208				} else {
209					regex = "(?<=(" + what + whitespace + "))" + identifier;
210				}
211			} else {
212				if (markMarker) {
213					regex = identifier + whitespace + what;
214				} else {
215					regex = identifier + "(?=(" + whitespace + what + "))";
216				}
217			}
218			return new XshdRule {
219				ColorReference = GetColorReference(el),
220				Regex = regex,
221				RegexType = XshdRegexType.IgnorePatternWhitespace
222			};
223		}
224		
225		XshdSpan ImportSpan(XmlElement element)
226		{
227			XshdSpan span = new XshdSpan();
228			if (element.HasAttribute("rule")) {
229				span.RuleSetReference = new XshdReference<XshdRuleSet>(null, element.GetAttribute("rule"));
230			}
231			char escapeCharacter = ruleSetEscapeCharacter;
232			if (element.HasAttribute("escapecharacter")) {
233				escapeCharacter = element.GetAttribute("escapecharacter")[0];
234			}
235			span.Multiline = !(element.GetBoolAttribute("stopateol") ?? false);
236			
237			span.SpanColorReference = GetColorReference(element);
238			
239			span.BeginRegexType = XshdRegexType.IgnorePatternWhitespace;
240			span.BeginRegex = ImportRegex(element["Begin"].InnerText,
241			                              element["Begin"].GetBoolAttribute("singleword") ?? false,
242			                              element["Begin"].GetBoolAttribute("startofline"));
243			span.BeginColorReference = GetColorReference(element["Begin"]);
244			
245			string endElementText = string.Empty;
246			if (element["End"] != null) {
247				span.EndRegexType = XshdRegexType.IgnorePatternWhitespace;
248				endElementText = element["End"].InnerText;
249				span.EndRegex = ImportRegex(endElementText,
250				                            element["End"].GetBoolAttribute("singleword") ?? false,
251				                            null);
252				span.EndColorReference = GetColorReference(element["End"]);
253			}
254			
255			if (escapeCharacter != '\0') {
256				XshdRuleSet ruleSet = new XshdRuleSet();
257				if (endElementText.Length == 1 && endElementText[0] == escapeCharacter) {
258					// ""-style escape
259					ruleSet.Elements.Add(new XshdSpan {
260					                     	BeginRegex = Regex.Escape(endElementText + endElementText),
261					                     	EndRegex = ""
262					                     });
263				} else {
264					// \"-style escape
265					ruleSet.Elements.Add(new XshdSpan {
266					                     	BeginRegex = Regex.Escape(escapeCharacter.ToString()),
267					                     	EndRegex = "."
268					                     });
269				}
270				if (span.RuleSetReference.ReferencedElement != null) {
271					ruleSet.Elements.Add(new XshdImport { RuleSetReference = span.RuleSetReference });
272				}
273				span.RuleSetReference = new XshdReference<XshdRuleSet>(ruleSet);
274			}
275			return span;
276		}
277		
278		static string ImportRegex(string expr, bool singleWord, bool? startOfLine)
279		{
280			StringBuilder b = new StringBuilder();
281			if (startOfLine != null) {
282				if (startOfLine.Value) {
283					b.Append(@"(?<=(^\s*))");
284				} else {
285					b.Append(@"(?<!(^\s*))");
286				}
287			} else {
288				if (singleWord)
289					b.Append(@"\b");
290			}
291			for (int i = 0; i < expr.Length; i++) {
292				char c = expr[i];
293				if (c == '@') {
294					++i;
295					if (i == expr.Length)
296						throw new HighlightingDefinitionInvalidException("Unexpected end of @ sequence, use @@ to look for a single @.");
297					switch (expr[i]) {
298						case 'C': // match whitespace or punctuation
299							b.Append(@"[^\w\d_]");
300							break;
301						case '!': // negative lookahead
302							{
303								StringBuilder whatmatch = new StringBuilder();
304								++i;
305								while (i < expr.Length && expr[i] != '@') {
306									whatmatch.Append(expr[i++]);
307								}
308								b.Append("(?!(");
309								b.Append(Regex.Escape(whatmatch.ToString()));
310								b.Append("))");
311							}
312							break;
313						case '-': // negative lookbehind
314							{
315								StringBuilder whatmatch = new StringBuilder();
316								++i;
317								while (i < expr.Length && expr[i] != '@') {
318									whatmatch.Append(expr[i++]);
319								}
320								b.Append("(?<!(");
321								b.Append(Regex.Escape(whatmatch.ToString()));
322								b.Append("))");
323							}
324							break;
325						case '@':
326							b.Append("@");
327							break;
328						default:
329							throw new HighlightingDefinitionInvalidException("Unknown character in @ sequence.");
330					}
331				} else {
332					b.Append(Regex.Escape(c.ToString()));
333				}
334			}
335			if (singleWord)
336				b.Append(@"\b");
337			return b.ToString();
338		}
339	}
340}