330 lines
10 KiB
C#
330 lines
10 KiB
C#
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
|
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Windows;
|
|
using System.Windows.Media;
|
|
using System.Xml;
|
|
using System.Xml.Schema;
|
|
|
|
using ICSharpCode.AvalonEdit.Utils;
|
|
|
|
namespace ICSharpCode.AvalonEdit.Highlighting.Xshd
|
|
{
|
|
/// <summary>
|
|
/// Loads .xshd files, version 2.0.
|
|
/// Version 2.0 files are recognized by the namespace.
|
|
/// </summary>
|
|
static class V2Loader
|
|
{
|
|
public const string Namespace = "http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008";
|
|
|
|
static XmlSchemaSet schemaSet;
|
|
|
|
static XmlSchemaSet SchemaSet {
|
|
get {
|
|
if (schemaSet == null) {
|
|
schemaSet = HighlightingLoader.LoadSchemaSet(new XmlTextReader(
|
|
Resources.OpenStream("ModeV2.xsd")));
|
|
}
|
|
return schemaSet;
|
|
}
|
|
}
|
|
|
|
public static XshdSyntaxDefinition LoadDefinition(XmlReader reader, bool skipValidation)
|
|
{
|
|
reader = HighlightingLoader.GetValidatingReader(reader, true, skipValidation ? null : SchemaSet);
|
|
reader.Read();
|
|
return ParseDefinition(reader);
|
|
}
|
|
|
|
static XshdSyntaxDefinition ParseDefinition(XmlReader reader)
|
|
{
|
|
Debug.Assert(reader.LocalName == "SyntaxDefinition");
|
|
XshdSyntaxDefinition def = new XshdSyntaxDefinition();
|
|
def.Name = reader.GetAttribute("name");
|
|
string extensions = reader.GetAttribute("extensions");
|
|
if (extensions != null)
|
|
def.Extensions.AddRange(extensions.Split(';'));
|
|
ParseElements(def.Elements, reader);
|
|
Debug.Assert(reader.NodeType == XmlNodeType.EndElement);
|
|
Debug.Assert(reader.LocalName == "SyntaxDefinition");
|
|
return def;
|
|
}
|
|
|
|
static void ParseElements(ICollection<XshdElement> c, XmlReader reader)
|
|
{
|
|
if (reader.IsEmptyElement)
|
|
return;
|
|
while (reader.Read() && reader.NodeType != XmlNodeType.EndElement) {
|
|
Debug.Assert(reader.NodeType == XmlNodeType.Element);
|
|
switch (reader.Name) {
|
|
case "RuleSet":
|
|
c.Add(ParseRuleSet(reader));
|
|
break;
|
|
case "Property":
|
|
c.Add(ParseProperty(reader));
|
|
break;
|
|
case "Color":
|
|
c.Add(ParseNamedColor(reader));
|
|
break;
|
|
case "Keywords":
|
|
c.Add(ParseKeywords(reader));
|
|
break;
|
|
case "Span":
|
|
c.Add(ParseSpan(reader));
|
|
break;
|
|
case "Import":
|
|
c.Add(ParseImport(reader));
|
|
break;
|
|
case "Rule":
|
|
c.Add(ParseRule(reader));
|
|
break;
|
|
default:
|
|
throw new NotSupportedException("Unknown element " + reader.Name);
|
|
}
|
|
}
|
|
}
|
|
|
|
static XshdElement ParseProperty(XmlReader reader)
|
|
{
|
|
XshdProperty property = new XshdProperty();
|
|
SetPosition(property, reader);
|
|
property.Name = reader.GetAttribute("name");
|
|
property.Value = reader.GetAttribute("value");
|
|
return property;
|
|
}
|
|
|
|
static XshdRuleSet ParseRuleSet(XmlReader reader)
|
|
{
|
|
XshdRuleSet ruleSet = new XshdRuleSet();
|
|
SetPosition(ruleSet, reader);
|
|
ruleSet.Name = reader.GetAttribute("name");
|
|
ruleSet.IgnoreCase = reader.GetBoolAttribute("ignoreCase");
|
|
|
|
CheckElementName(reader, ruleSet.Name);
|
|
ParseElements(ruleSet.Elements, reader);
|
|
return ruleSet;
|
|
}
|
|
|
|
static XshdRule ParseRule(XmlReader reader)
|
|
{
|
|
XshdRule rule = new XshdRule();
|
|
SetPosition(rule, reader);
|
|
rule.ColorReference = ParseColorReference(reader);
|
|
if (!reader.IsEmptyElement) {
|
|
reader.Read();
|
|
if (reader.NodeType == XmlNodeType.Text) {
|
|
rule.Regex = reader.ReadContentAsString();
|
|
rule.RegexType = XshdRegexType.IgnorePatternWhitespace;
|
|
}
|
|
}
|
|
return rule;
|
|
}
|
|
|
|
static XshdKeywords ParseKeywords(XmlReader reader)
|
|
{
|
|
XshdKeywords keywords = new XshdKeywords();
|
|
SetPosition(keywords, reader);
|
|
keywords.ColorReference = ParseColorReference(reader);
|
|
reader.Read();
|
|
while (reader.NodeType != XmlNodeType.EndElement) {
|
|
Debug.Assert(reader.NodeType == XmlNodeType.Element);
|
|
keywords.Words.Add(reader.ReadElementString());
|
|
}
|
|
return keywords;
|
|
}
|
|
|
|
static XshdImport ParseImport(XmlReader reader)
|
|
{
|
|
XshdImport import = new XshdImport();
|
|
SetPosition(import, reader);
|
|
import.RuleSetReference = ParseRuleSetReference(reader);
|
|
if (!reader.IsEmptyElement)
|
|
reader.Skip();
|
|
return import;
|
|
}
|
|
|
|
static XshdSpan ParseSpan(XmlReader reader)
|
|
{
|
|
XshdSpan span = new XshdSpan();
|
|
SetPosition(span, reader);
|
|
span.BeginRegex = reader.GetAttribute("begin");
|
|
span.EndRegex = reader.GetAttribute("end");
|
|
span.Multiline = reader.GetBoolAttribute("multiline") ?? false;
|
|
span.SpanColorReference = ParseColorReference(reader);
|
|
span.RuleSetReference = ParseRuleSetReference(reader);
|
|
if (!reader.IsEmptyElement) {
|
|
reader.Read();
|
|
while (reader.NodeType != XmlNodeType.EndElement) {
|
|
Debug.Assert(reader.NodeType == XmlNodeType.Element);
|
|
switch (reader.Name) {
|
|
case "Begin":
|
|
if (span.BeginRegex != null)
|
|
throw Error(reader, "Duplicate Begin regex");
|
|
span.BeginColorReference = ParseColorReference(reader);
|
|
span.BeginRegex = reader.ReadElementString();
|
|
span.BeginRegexType = XshdRegexType.IgnorePatternWhitespace;
|
|
break;
|
|
case "End":
|
|
if (span.EndRegex != null)
|
|
throw Error(reader, "Duplicate End regex");
|
|
span.EndColorReference = ParseColorReference(reader);
|
|
span.EndRegex = reader.ReadElementString();
|
|
span.EndRegexType = XshdRegexType.IgnorePatternWhitespace;
|
|
break;
|
|
case "RuleSet":
|
|
if (span.RuleSetReference.ReferencedElement != null)
|
|
throw Error(reader, "Cannot specify both inline RuleSet and RuleSet reference");
|
|
span.RuleSetReference = new XshdReference<XshdRuleSet>(ParseRuleSet(reader));
|
|
reader.Read();
|
|
break;
|
|
default:
|
|
throw new NotSupportedException("Unknown element " + reader.Name);
|
|
}
|
|
}
|
|
}
|
|
return span;
|
|
}
|
|
|
|
static Exception Error(XmlReader reader, string message)
|
|
{
|
|
return Error(reader as IXmlLineInfo, message);
|
|
}
|
|
|
|
static Exception Error(IXmlLineInfo lineInfo, string message)
|
|
{
|
|
if (lineInfo != null)
|
|
return new HighlightingDefinitionInvalidException(HighlightingLoader.FormatExceptionMessage(message, lineInfo.LineNumber, lineInfo.LinePosition));
|
|
else
|
|
return new HighlightingDefinitionInvalidException(message);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the element's position to the XmlReader's position.
|
|
/// </summary>
|
|
static void SetPosition(XshdElement element, XmlReader reader)
|
|
{
|
|
IXmlLineInfo lineInfo = reader as IXmlLineInfo;
|
|
if (lineInfo != null) {
|
|
element.LineNumber = lineInfo.LineNumber;
|
|
element.ColumnNumber = lineInfo.LinePosition;
|
|
}
|
|
}
|
|
|
|
static XshdReference<XshdRuleSet> ParseRuleSetReference(XmlReader reader)
|
|
{
|
|
string ruleSet = reader.GetAttribute("ruleSet");
|
|
if (ruleSet != null) {
|
|
// '/' is valid in highlighting definition names, so we need the last occurence
|
|
int pos = ruleSet.LastIndexOf('/');
|
|
if (pos >= 0) {
|
|
return new XshdReference<XshdRuleSet>(ruleSet.Substring(0, pos), ruleSet.Substring(pos + 1));
|
|
} else {
|
|
return new XshdReference<XshdRuleSet>(null, ruleSet);
|
|
}
|
|
} else {
|
|
return new XshdReference<XshdRuleSet>();
|
|
}
|
|
}
|
|
|
|
static void CheckElementName(XmlReader reader, string name)
|
|
{
|
|
if (name != null) {
|
|
if (name.Length == 0)
|
|
throw Error(reader, "The empty string is not a valid name.");
|
|
if (name.IndexOf('/') >= 0)
|
|
throw Error(reader, "Element names must not contain a slash.");
|
|
}
|
|
}
|
|
|
|
#region ParseColor
|
|
static XshdColor ParseNamedColor(XmlReader reader)
|
|
{
|
|
XshdColor color = ParseColorAttributes(reader);
|
|
// check removed: invisible named colors may be useful now that apps can read highlighting data
|
|
//if (color.Foreground == null && color.FontWeight == null && color.FontStyle == null)
|
|
// throw Error(reader, "A named color must have at least one element.");
|
|
color.Name = reader.GetAttribute("name");
|
|
CheckElementName(reader, color.Name);
|
|
color.ExampleText = reader.GetAttribute("exampleText");
|
|
return color;
|
|
}
|
|
|
|
static XshdReference<XshdColor> ParseColorReference(XmlReader reader)
|
|
{
|
|
string color = reader.GetAttribute("color");
|
|
if (color != null) {
|
|
int pos = color.LastIndexOf('/');
|
|
if (pos >= 0) {
|
|
return new XshdReference<XshdColor>(color.Substring(0, pos), color.Substring(pos + 1));
|
|
} else {
|
|
return new XshdReference<XshdColor>(null, color);
|
|
}
|
|
} else {
|
|
return new XshdReference<XshdColor>(ParseColorAttributes(reader));
|
|
}
|
|
}
|
|
|
|
static XshdColor ParseColorAttributes(XmlReader reader)
|
|
{
|
|
XshdColor color = new XshdColor();
|
|
SetPosition(color, reader);
|
|
IXmlLineInfo position = reader as IXmlLineInfo;
|
|
color.Foreground = ParseColor(position, reader.GetAttribute("foreground"));
|
|
color.Background = ParseColor(position, reader.GetAttribute("background"));
|
|
color.FontWeight = ParseFontWeight(reader.GetAttribute("fontWeight"));
|
|
color.FontStyle = ParseFontStyle(reader.GetAttribute("fontStyle"));
|
|
return color;
|
|
}
|
|
|
|
internal readonly static ColorConverter ColorConverter = new ColorConverter();
|
|
internal readonly static FontWeightConverter FontWeightConverter = new FontWeightConverter();
|
|
internal readonly static FontStyleConverter FontStyleConverter = new FontStyleConverter();
|
|
|
|
static HighlightingBrush ParseColor(IXmlLineInfo lineInfo, string color)
|
|
{
|
|
if (string.IsNullOrEmpty(color))
|
|
return null;
|
|
if (color.StartsWith("SystemColors.", StringComparison.Ordinal))
|
|
return GetSystemColorBrush(lineInfo, color);
|
|
else
|
|
return FixedColorHighlightingBrush((Color?)ColorConverter.ConvertFromInvariantString(color));
|
|
}
|
|
|
|
internal static SystemColorHighlightingBrush GetSystemColorBrush(IXmlLineInfo lineInfo, string name)
|
|
{
|
|
Debug.Assert(name.StartsWith("SystemColors.", StringComparison.Ordinal));
|
|
string shortName = name.Substring(13);
|
|
var property = typeof(SystemColors).GetProperty(shortName + "Brush");
|
|
if (property == null)
|
|
throw Error(lineInfo, "Cannot find '" + name + "'.");
|
|
return new SystemColorHighlightingBrush(property);
|
|
}
|
|
|
|
static HighlightingBrush FixedColorHighlightingBrush(Color? color)
|
|
{
|
|
if (color == null)
|
|
return null;
|
|
return new SimpleHighlightingBrush(color.Value);
|
|
}
|
|
|
|
static FontWeight? ParseFontWeight(string fontWeight)
|
|
{
|
|
if (string.IsNullOrEmpty(fontWeight))
|
|
return null;
|
|
return (FontWeight?)FontWeightConverter.ConvertFromInvariantString(fontWeight);
|
|
}
|
|
|
|
static FontStyle? ParseFontStyle(string fontStyle)
|
|
{
|
|
if (string.IsNullOrEmpty(fontStyle))
|
|
return null;
|
|
return (FontStyle?)FontStyleConverter.ConvertFromInvariantString(fontStyle);
|
|
}
|
|
#endregion
|
|
}
|
|
}
|