This commit is contained in:
jkuehner 2019-06-10 17:48:18 +02:00
Родитель cb8e7377c5
Коммит 3e85f0908c
4 изменённых файлов: 363 добавлений и 290 удалений

Просмотреть файл

@ -20,7 +20,6 @@ using System;
using System.Globalization;
using System.Linq;
using System.Runtime.Serialization;
using System.Security.Permissions;
using System.Text;
using System.Windows;
using System.Windows.Media;
@ -35,102 +34,114 @@ namespace ICSharpCode.AvalonEdit.Highlighting
public class HighlightingColor : ISerializable, IFreezable, ICloneable, IEquatable<HighlightingColor>
{
internal static readonly HighlightingColor Empty = FreezableHelper.FreezeAndReturn(new HighlightingColor());
string name;
FontFamily fontFamily = null;
int? fontSize;
FontWeight? fontWeight;
FontFamily fontFamily = null;
int? fontSize;
FontWeight? fontWeight;
FontStyle? fontStyle;
bool? underline;
bool? strikethrough;
HighlightingBrush foreground;
HighlightingBrush background;
bool frozen;
/// <summary>
/// Gets/Sets the name of the color.
/// </summary>
public string Name {
get {
public string Name
{
get
{
return name;
}
set {
set
{
if (frozen)
throw new InvalidOperationException();
name = value;
}
}
/// <summary>
/// Gets/sets the font family. Null if the highlighting color does not change the font style.
/// </summary>
public FontFamily FontFamily
{
get
{
return fontFamily;
}
set
{
if (frozen)
throw new InvalidOperationException();
fontFamily = value;
}
}
/// <summary>
/// Gets/sets the font family. Null if the highlighting color does not change the font style.
/// </summary>
public FontFamily FontFamily
{
get
{
return fontFamily;
}
set
{
if (frozen)
throw new InvalidOperationException();
fontFamily = value;
}
}
/// <summary>
/// Gets/sets the font size. Null if the highlighting color does not change the font style.
/// </summary>
public int? FontSize
{
get
{
return fontSize;
}
set
{
if (frozen)
throw new InvalidOperationException();
fontSize = value;
}
}
/// <summary>
/// Gets/sets the font size. Null if the highlighting color does not change the font style.
/// </summary>
public int? FontSize
{
get
{
return fontSize;
}
set
{
if (frozen)
throw new InvalidOperationException();
fontSize = value;
}
}
/// <summary>
/// Gets/sets the font weight. Null if the highlighting color does not change the font weight.
/// </summary>
public FontWeight? FontWeight {
get {
/// <summary>
/// Gets/sets the font weight. Null if the highlighting color does not change the font weight.
/// </summary>
public FontWeight? FontWeight
{
get
{
return fontWeight;
}
set {
set
{
if (frozen)
throw new InvalidOperationException();
fontWeight = value;
}
}
/// <summary>
/// Gets/sets the font style. Null if the highlighting color does not change the font style.
/// </summary>
public FontStyle? FontStyle {
get {
public FontStyle? FontStyle
{
get
{
return fontStyle;
}
set {
set
{
if (frozen)
throw new InvalidOperationException();
fontStyle = value;
}
}
/// <summary>
/// Gets/sets the underline flag. Null if the underline status does not change the font style.
/// </summary>
public bool? Underline {
get {
public bool? Underline
{
get
{
return underline;
}
set {
set
{
if (frozen)
throw new InvalidOperationException();
underline = value;
@ -157,38 +168,44 @@ namespace ICSharpCode.AvalonEdit.Highlighting
/// <summary>
/// Gets/sets the foreground color applied by the highlighting.
/// </summary>
public HighlightingBrush Foreground {
get {
public HighlightingBrush Foreground
{
get
{
return foreground;
}
set {
set
{
if (frozen)
throw new InvalidOperationException();
foreground = value;
}
}
/// <summary>
/// Gets/sets the background color applied by the highlighting.
/// </summary>
public HighlightingBrush Background {
get {
public HighlightingBrush Background
{
get
{
return background;
}
set {
set
{
if (frozen)
throw new InvalidOperationException();
background = value;
}
}
/// <summary>
/// Creates a new HighlightingColor instance.
/// </summary>
public HighlightingColor()
{
}
/// <summary>
/// Deserializes a HighlightingColor.
/// </summary>
@ -207,15 +224,15 @@ namespace ICSharpCode.AvalonEdit.Highlighting
this.Strikethrough = info.GetBoolean("Strikethrough");
this.Foreground = (HighlightingBrush)info.GetValue("Foreground", typeof(HighlightingBrush));
this.Background = (HighlightingBrush)info.GetValue("Background", typeof(HighlightingBrush));
if (info.GetBoolean("HasFamily"))
this.FontFamily = new FontFamily(info.GetString("Family"));
if (info.GetBoolean("HasSize"))
this.FontSize = info.GetInt32("Size");
}
if (info.GetBoolean("HasFamily"))
this.FontFamily = new FontFamily(info.GetString("Family"));
if (info.GetBoolean("HasSize"))
this.FontSize = info.GetInt32("Size");
}
/// <summary>
/// Serializes this HighlightingColor instance.
/// </summary>
/// <summary>
/// Serializes this HighlightingColor instance.
/// </summary>
[System.Security.SecurityCritical]
public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
{
@ -235,33 +252,37 @@ namespace ICSharpCode.AvalonEdit.Highlighting
info.AddValue("Strikethrough", this.Strikethrough.Value);
info.AddValue("Foreground", this.Foreground);
info.AddValue("Background", this.Background);
info.AddValue("HasFamily", this.FontFamily != null);
if (this.FontFamily != null)
info.AddValue("Family", this.FontFamily.FamilyNames.FirstOrDefault());
info.AddValue("HasSize", this.FontSize.HasValue);
if (this.FontSize.HasValue)
info.AddValue("Size", this.FontSize.Value.ToString());
}
info.AddValue("HasFamily", this.FontFamily != null);
if (this.FontFamily != null)
info.AddValue("Family", this.FontFamily.FamilyNames.FirstOrDefault());
info.AddValue("HasSize", this.FontSize.HasValue);
if (this.FontSize.HasValue)
info.AddValue("Size", this.FontSize.Value.ToString());
}
/// <summary>
/// Gets CSS code for the color.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "CSS usually uses lowercase, and all possible values are English-only")]
/// <summary>
/// Gets CSS code for the color.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "CSS usually uses lowercase, and all possible values are English-only")]
public virtual string ToCss()
{
StringBuilder b = new StringBuilder();
if (Foreground != null) {
if (Foreground != null)
{
Color? c = Foreground.GetColor(null);
if (c != null) {
if (c != null)
{
b.AppendFormat(CultureInfo.InvariantCulture, "color: #{0:x2}{1:x2}{2:x2}; ", c.Value.R, c.Value.G, c.Value.B);
}
}
if (FontWeight != null) {
if (FontWeight != null)
{
b.Append("font-weight: ");
b.Append(FontWeight.Value.ToString().ToLowerInvariant());
b.Append("; ");
}
if (FontStyle != null) {
if (FontStyle != null)
{
b.Append("font-style: ");
b.Append(FontStyle.Value.ToString().ToLowerInvariant());
b.Append("; ");
@ -283,13 +304,13 @@ namespace ICSharpCode.AvalonEdit.Highlighting
}
return b.ToString();
}
/// <inheritdoc/>
public override string ToString()
{
return "[" + GetType().Name + " " + (string.IsNullOrEmpty(this.Name) ? ToCss() : this.Name) + "]";
}
/// <summary>
/// Prevent further changes to this highlighting color.
/// </summary>
@ -297,14 +318,15 @@ namespace ICSharpCode.AvalonEdit.Highlighting
{
frozen = true;
}
/// <summary>
/// Gets whether this HighlightingColor instance is frozen.
/// </summary>
public bool IsFrozen {
public bool IsFrozen
{
get { return frozen; }
}
/// <summary>
/// Clones this highlighting color.
/// If this color is frozen, the clone will be unfrozen.
@ -315,18 +337,18 @@ namespace ICSharpCode.AvalonEdit.Highlighting
c.frozen = false;
return c;
}
object ICloneable.Clone()
{
return Clone();
}
/// <inheritdoc/>
public override sealed bool Equals(object obj)
{
return Equals(obj as HighlightingColor);
}
/// <inheritdoc/>
public virtual bool Equals(HighlightingColor other)
{
@ -335,30 +357,31 @@ namespace ICSharpCode.AvalonEdit.Highlighting
return this.name == other.name && this.fontWeight == other.fontWeight
&& this.fontStyle == other.fontStyle && this.underline == other.underline && this.strikethrough == other.strikethrough
&& object.Equals(this.foreground, other.foreground) && object.Equals(this.background, other.background)
&& object.Equals(this.fontFamily, other.fontFamily) && object.Equals(this.FontSize, other.FontSize);
&& object.Equals(this.fontFamily, other.fontFamily) && object.Equals(this.FontSize, other.FontSize);
}
/// <inheritdoc/>
public override int GetHashCode()
{
int hashCode = 0;
unchecked {
unchecked
{
if (name != null)
hashCode += 1000000007 * name.GetHashCode();
hashCode += 1000000009 * fontWeight.GetHashCode();
hashCode += 1000000021 * fontStyle.GetHashCode();
if (foreground != null)
hashCode += 1000000033 * foreground.GetHashCode();
if (background != null)
hashCode += 1000000087 * background.GetHashCode();
if (fontFamily != null)
hashCode += 1000000123 * fontFamily.GetHashCode();
if (fontSize != null)
hashCode += 1000000167 * fontSize.GetHashCode();
}
return hashCode;
if (background != null)
hashCode += 1000000087 * background.GetHashCode();
if (fontFamily != null)
hashCode += 1000000123 * fontFamily.GetHashCode();
if (fontSize != null)
hashCode += 1000000167 * fontSize.GetHashCode();
}
return hashCode;
}
/// <summary>
/// Overwrites the properties in this HighlightingColor with those from the given color;
/// but maintains the current values where the properties of the given color return <c>null</c>.
@ -374,21 +397,23 @@ namespace ICSharpCode.AvalonEdit.Highlighting
this.foreground = color.foreground;
if (color.background != null)
this.background = color.background;
if (color.underline != null)
this.underline = color.underline;
if (color.strikethrough != null)
this.strikethrough = color.strikethrough;
if (color.underline != null)
this.underline = color.underline;
if (color.strikethrough != null)
this.strikethrough = color.strikethrough;
if (color.fontFamily != null)
this.fontFamily = color.fontFamily;
if (color.fontSize != null)
this.fontSize = color.fontSize;
}
this.fontFamily = color.fontFamily;
if (color.fontSize != null)
this.fontSize = color.fontSize;
}
internal bool IsEmptyForMerge {
get {
return fontWeight == null && fontStyle == null && underline == null
&& strikethrough == null && foreground == null && background == null
&& fontFamily == null && fontSize == null;
internal bool IsEmptyForMerge
{
get
{
return fontWeight == null && fontStyle == null && underline == null
&& strikethrough == null && foreground == null && background == null
&& fontFamily == null && fontSize == null;
}
}
}

Просмотреть файл

@ -34,7 +34,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting
TextView textView;
IHighlighter highlighter;
bool isFixedHighlighter;
/// <summary>
/// Creates a new HighlightingColorizer instance.
/// </summary>
@ -45,7 +45,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting
throw new ArgumentNullException("definition");
this.definition = definition;
}
/// <summary>
/// Creates a new HighlightingColorizer instance that uses a fixed highlighter instance.
/// The colorizer can only be used with text views that show the document for which
@ -59,7 +59,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting
this.highlighter = highlighter;
this.isFixedHighlighter = true;
}
/// <summary>
/// Creates a new HighlightingColorizer instance.
/// Derived classes using this constructor must override the <see cref="CreateHighlighter"/> method.
@ -67,22 +67,24 @@ namespace ICSharpCode.AvalonEdit.Highlighting
protected HighlightingColorizer()
{
}
void textView_DocumentChanged(object sender, EventArgs e)
{
TextView textView = (TextView)sender;
DeregisterServices(textView);
RegisterServices(textView);
}
/// <summary>
/// This method is called when a text view is removed from this HighlightingColorizer,
/// and also when the TextDocument on any associated text view changes.
/// </summary>
protected virtual void DeregisterServices(TextView textView)
{
if (highlighter != null) {
if (isInHighlightingGroup) {
if (highlighter != null)
{
if (isInHighlightingGroup)
{
highlighter.EndHighlighting();
isInHighlightingGroup = false;
}
@ -90,33 +92,37 @@ namespace ICSharpCode.AvalonEdit.Highlighting
// remove highlighter if it is registered
if (textView.Services.GetService(typeof(IHighlighter)) == highlighter)
textView.Services.RemoveService(typeof(IHighlighter));
if (!isFixedHighlighter) {
if (!isFixedHighlighter)
{
if (highlighter != null)
highlighter.Dispose();
highlighter = null;
}
}
}
/// <summary>
/// This method is called when a new text view is added to this HighlightingColorizer,
/// and also when the TextDocument on any associated text view changes.
/// </summary>
protected virtual void RegisterServices(TextView textView)
{
if (textView.Document != null) {
if (textView.Document != null)
{
if (!isFixedHighlighter)
highlighter = textView.Document != null ? CreateHighlighter(textView, textView.Document) : null;
if (highlighter != null && highlighter.Document == textView.Document) {
if (highlighter != null && highlighter.Document == textView.Document)
{
// add service only if it doesn't already exist
if (textView.Services.GetService(typeof(IHighlighter)) == null) {
if (textView.Services.GetService(typeof(IHighlighter)) == null)
{
textView.Services.AddService(typeof(IHighlighter), highlighter);
}
highlighter.HighlightingStateChanged += OnHighlightStateChanged;
}
}
}
/// <summary>
/// Creates the IHighlighter instance for the specified text document.
/// </summary>
@ -127,11 +133,12 @@ namespace ICSharpCode.AvalonEdit.Highlighting
else
throw new NotSupportedException("Cannot create a highlighter because no IHighlightingDefinition was specified, and the CreateHighlighter() method was not overridden.");
}
/// <inheritdoc/>
protected override void OnAddToTextView(TextView textView)
{
if (this.textView != null) {
if (this.textView != null)
{
throw new InvalidOperationException("Cannot use a HighlightingColorizer instance in multiple text views. Please create a separate instance for each text view.");
}
base.OnAddToTextView(textView);
@ -141,7 +148,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting
textView.VisualLinesChanged += textView_VisualLinesChanged;
RegisterServices(textView);
}
/// <inheritdoc/>
protected override void OnRemoveFromTextView(TextView textView)
{
@ -152,18 +159,20 @@ namespace ICSharpCode.AvalonEdit.Highlighting
base.OnRemoveFromTextView(textView);
this.textView = null;
}
bool isInHighlightingGroup;
void textView_VisualLineConstructionStarting(object sender, VisualLineConstructionStartEventArgs e)
{
if (highlighter != null) {
if (highlighter != null)
{
// Force update of highlighting state up to the position where we start generating visual lines.
// This is necessary in case the document gets modified above the FirstLineInView so that the highlighting state changes.
// We need to detect this case and issue a redraw (through OnHighlightStateChanged)
// before the visual line construction reuses existing lines that were built using the invalid highlighting state.
lineNumberBeingColorized = e.FirstLineInView.LineNumber - 1;
if (!isInHighlightingGroup) {
if (!isInHighlightingGroup)
{
// avoid opening group twice if there was an exception during the previous visual line construction
// (not ideal, but better than throwing InvalidOperationException "group already open"
// without any way of recovering)
@ -174,24 +183,27 @@ namespace ICSharpCode.AvalonEdit.Highlighting
lineNumberBeingColorized = 0;
}
}
void textView_VisualLinesChanged(object sender, EventArgs e)
{
if (highlighter != null && isInHighlightingGroup) {
if (highlighter != null && isInHighlightingGroup)
{
highlighter.EndHighlighting();
isInHighlightingGroup = false;
}
}
DocumentLine lastColorizedLine;
/// <inheritdoc/>
protected override void Colorize(ITextRunConstructionContext context)
{
this.lastColorizedLine = null;
base.Colorize(context);
if (this.lastColorizedLine != context.VisualLine.LastDocumentLine) {
if (highlighter != null) {
if (this.lastColorizedLine != context.VisualLine.LastDocumentLine)
{
if (highlighter != null)
{
// In some cases, it is possible that we didn't highlight the last document line within the visual line
// (e.g. when the line ends with a fold marker).
// But even if we didn't highlight it, we'll have to update the highlighting state for it so that the
@ -203,26 +215,28 @@ namespace ICSharpCode.AvalonEdit.Highlighting
}
this.lastColorizedLine = null;
}
int lineNumberBeingColorized;
/// <inheritdoc/>
protected override void ColorizeLine(DocumentLine line)
{
if (highlighter != null) {
if (highlighter != null)
{
lineNumberBeingColorized = line.LineNumber;
HighlightedLine hl = highlighter.HighlightLine(lineNumberBeingColorized);
lineNumberBeingColorized = 0;
foreach (HighlightedSection section in hl.Sections) {
foreach (HighlightedSection section in hl.Sections)
{
if (IsEmptyColor(section.Color))
continue;
ChangeLinePart(section.Offset, section.Offset + section.Length,
visualLineElement => ApplyColorToElement(visualLineElement, section.Color));
visualLineElement => ApplyColorToElement(visualLineElement, section.Color));
}
}
this.lastColorizedLine = line;
}
/// <summary>
/// Gets whether the color is empty (has no effect on a VisualLineTextElement).
/// For example, the C# "Punctuation" is an empty color.
@ -235,7 +249,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting
&& color.FontStyle == null && color.FontWeight == null
&& color.Underline == null && color.Strikethrough == null;
}
/// <summary>
/// Applies a highlighting color to a visual line element.
/// </summary>
@ -243,36 +257,39 @@ namespace ICSharpCode.AvalonEdit.Highlighting
{
ApplyColorToElement(element, color, CurrentContext);
}
internal static void ApplyColorToElement(VisualLineElement element, HighlightingColor color, ITextRunConstructionContext context)
{
if (color.Foreground != null) {
if (color.Foreground != null)
{
Brush b = color.Foreground.GetBrush(context);
if (b != null)
element.TextRunProperties.SetForegroundBrush(b);
}
if (color.Background != null) {
if (color.Background != null)
{
Brush b = color.Background.GetBrush(context);
if (b != null)
element.BackgroundBrush = b;
}
if (color.FontStyle != null || color.FontWeight != null || color.FontFamily != null) {
if (color.FontStyle != null || color.FontWeight != null || color.FontFamily != null)
{
Typeface tf = element.TextRunProperties.Typeface;
element.TextRunProperties.SetTypeface(new Typeface(
color.FontFamily ?? tf.FontFamily,
color.FontStyle ?? tf.Style,
color.FontWeight ?? tf.Weight,
tf.Stretch
));
element.TextRunProperties.SetTypeface(new Typeface(
color.FontFamily ?? tf.FontFamily,
color.FontStyle ?? tf.Style,
color.FontWeight ?? tf.Weight,
tf.Stretch
));
}
if(color.Underline ?? false)
if (color.Underline ?? false)
element.TextRunProperties.SetTextDecorations(TextDecorations.Underline);
if (color.Strikethrough ?? false)
element.TextRunProperties.SetTextDecorations(TextDecorations.Strikethrough);
if (color.FontSize.HasValue)
element.TextRunProperties.SetFontRenderingEmSize(color.FontSize.Value);
element.TextRunProperties.SetFontRenderingEmSize(color.FontSize.Value);
}
/// <summary>
/// This method is responsible for telling the TextView to redraw lines when the highlighting state has changed.
/// </summary>
@ -283,47 +300,49 @@ namespace ICSharpCode.AvalonEdit.Highlighting
/// </remarks>
void OnHighlightStateChanged(int fromLineNumber, int toLineNumber)
{
if (lineNumberBeingColorized != 0) {
if (lineNumberBeingColorized != 0)
{
// Ignore notifications for any line except the one we're interested in.
// This improves the performance as Redraw() can take quite some time when called repeatedly
// while scanning the document (above the visible area) for highlighting changes.
if (toLineNumber <= lineNumberBeingColorized) {
if (toLineNumber <= lineNumberBeingColorized)
{
return;
}
}
// The user may have inserted "/*" into the current line, and so far only that line got redrawn.
// So when the highlighting state is changed, we issue a redraw for the line immediately below.
// If the highlighting state change applies to the lines below, too, the construction of each line
// will invalidate the next line, and the construction pass will regenerate all lines.
Debug.WriteLine(string.Format("OnHighlightStateChanged forces redraw of lines {0} to {1}", fromLineNumber, toLineNumber));
// If the VisualLine construction is in progress, we have to avoid sending redraw commands for
// anything above the line currently being constructed.
// It takes some explanation to see why this cannot happen.
// VisualLines always get constructed from top to bottom.
// Each VisualLine construction calls into the highlighter and thus forces an update of the
// highlighting state for all lines up to the one being constructed.
// To guarantee that we don't redraw lines we just constructed, we need to show that when
// a VisualLine is being reused, the highlighting state at that location is still up-to-date.
// This isn't exactly trivial and the initial implementation was incorrect in the presence of external document changes
// (e.g. split view).
// For the first line in the view, the TextView.VisualLineConstructionStarting event is used to check that the
// highlighting state is up-to-date. If it isn't, this method will be executed, and it'll mark the first line
// in the view as requiring a redraw. This is safely possible because that event occurs before any lines are reused.
// Once we take care of the first visual line, we won't get in trouble with other lines due to the top-to-bottom
// construction process.
// We'll prove that: if line N is being reused, then the highlighting state is up-to-date until (end of) line N-1.
// Start of induction: the first line in view is reused only if the highlighting state was up-to-date
// until line N-1 (no change detected in VisualLineConstructionStarting event).
// Induction step:
// If another line N+1 is being reused, then either
// a) the previous line (the visual line containing document line N) was newly constructed
@ -332,15 +351,18 @@ namespace ICSharpCode.AvalonEdit.Highlighting
// In case b, the highlighting state at N-1 was up-to-date, and the text of line N was not changed.
// (if the text was changed, the line could not have been reused).
// From this follows that the highlighting state at N is still up-to-date.
// The above proof holds even in the presence of folding: folding only ever hides text in the middle of a visual line.
// Our Colorize-override ensures that the highlighting state is always updated for the LastDocumentLine,
// so it will always invalidate the next visual line when a folded line is constructed
// and the highlighting stack has changed.
if (fromLineNumber == toLineNumber) {
if (fromLineNumber == toLineNumber)
{
textView.Redraw(textView.Document.GetLineByNumber(fromLineNumber));
} else {
}
else
{
// If there are multiple lines marked as changed; only the first one really matters
// for the highlighting during rendering.
// However this callback is also called outside of the rendering process, e.g. when a highlighter
@ -350,7 +372,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting
int startOffset = fromLine.Offset;
textView.Redraw(startOffset, toLine.EndOffset - startOffset);
}
/*
* Meta-comment: "why does this have to be so complicated?"
*

Просмотреть файл

@ -35,26 +35,29 @@ namespace ICSharpCode.AvalonEdit.Highlighting.Xshd
static class V2Loader
{
public const string Namespace = "http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008";
static XmlSchemaSet schemaSet;
static XmlSchemaSet SchemaSet {
get {
if (schemaSet == null) {
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");
@ -68,19 +71,22 @@ namespace ICSharpCode.AvalonEdit.Highlighting.Xshd
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) {
while (reader.Read() && reader.NodeType != XmlNodeType.EndElement)
{
Debug.Assert(reader.NodeType == XmlNodeType.Element);
if (reader.NamespaceURI != Namespace) {
if (reader.NamespaceURI != Namespace)
{
if (!reader.IsEmptyElement)
reader.Skip();
continue;
}
switch (reader.Name) {
switch (reader.Name)
{
case "RuleSet":
c.Add(ParseRuleSet(reader));
break;
@ -107,7 +113,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting.Xshd
}
}
}
static XshdElement ParseProperty(XmlReader reader)
{
XshdProperty property = new XshdProperty();
@ -116,47 +122,50 @@ namespace ICSharpCode.AvalonEdit.Highlighting.Xshd
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) {
if (!reader.IsEmptyElement)
{
reader.Read();
if (reader.NodeType == XmlNodeType.Text) {
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) {
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();
@ -166,7 +175,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting.Xshd
reader.Skip();
return import;
}
static XshdSpan ParseSpan(XmlReader reader)
{
XshdSpan span = new XshdSpan();
@ -176,11 +185,14 @@ namespace ICSharpCode.AvalonEdit.Highlighting.Xshd
span.Multiline = reader.GetBoolAttribute("multiline") ?? false;
span.SpanColorReference = ParseColorReference(reader);
span.RuleSetReference = ParseRuleSetReference(reader);
if (!reader.IsEmptyElement) {
if (!reader.IsEmptyElement)
{
reader.Read();
while (reader.NodeType != XmlNodeType.EndElement) {
while (reader.NodeType != XmlNodeType.EndElement)
{
Debug.Assert(reader.NodeType == XmlNodeType.Element);
switch (reader.Name) {
switch (reader.Name)
{
case "Begin":
if (span.BeginRegex != null)
throw Error(reader, "Duplicate Begin regex");
@ -208,12 +220,12 @@ namespace ICSharpCode.AvalonEdit.Highlighting.Xshd
}
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)
@ -221,45 +233,53 @@ namespace ICSharpCode.AvalonEdit.Highlighting.Xshd
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) {
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) {
if (ruleSet != null)
{
// '/' is valid in highlighting definition names, so we need the last occurence
int pos = ruleSet.LastIndexOf('/');
if (pos >= 0) {
if (pos >= 0)
{
return new XshdReference<XshdRuleSet>(ruleSet.Substring(0, pos), ruleSet.Substring(pos + 1));
} else {
}
else
{
return new XshdReference<XshdRuleSet>(null, ruleSet);
}
} else {
}
else
{
return new XshdReference<XshdRuleSet>();
}
}
static void CheckElementName(XmlReader reader, string name)
{
if (name != null) {
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)
{
@ -272,22 +292,28 @@ namespace ICSharpCode.AvalonEdit.Highlighting.Xshd
color.ExampleText = reader.GetAttribute("exampleText");
return color;
}
static XshdReference<XshdColor> ParseColorReference(XmlReader reader)
{
string color = reader.GetAttribute("color");
if (color != null) {
if (color != null)
{
int pos = color.LastIndexOf('/');
if (pos >= 0) {
if (pos >= 0)
{
return new XshdReference<XshdColor>(color.Substring(0, pos), color.Substring(pos + 1));
} else {
}
else
{
return new XshdReference<XshdColor>(null, color);
}
} else {
}
else
{
return new XshdReference<XshdColor>(ParseColorAttributes(reader));
}
}
static XshdColor ParseColorAttributes(XmlReader reader)
{
XshdColor color = new XshdColor();
@ -300,45 +326,45 @@ namespace ICSharpCode.AvalonEdit.Highlighting.Xshd
color.Underline = reader.GetBoolAttribute("underline");
color.Strikethrough = reader.GetBoolAttribute("strikethrough");
color.FontFamily = ParseFontFamily(position, reader.GetAttribute("fontFamily"));
color.FontSize = ParseFontSize(position, reader.GetAttribute("fontSize"));
return color;
color.FontSize = ParseFontSize(position, reader.GetAttribute("fontSize"));
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));
}
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));
}
static int? ParseFontSize(IXmlLineInfo lineInfo, string size)
{
int value;
return int.TryParse(size, out value)
? value
: (int?)null;
}
static int? ParseFontSize(IXmlLineInfo lineInfo, string size)
{
int value;
return int.TryParse(size, out value)
? value
: (int?)null;
}
static FontFamily ParseFontFamily(IXmlLineInfo lineInfo, string family)
{
if (!string.IsNullOrEmpty(family))
{
return new FontFamily(family);
}
else
{
return null;
}
}
static FontFamily ParseFontFamily(IXmlLineInfo lineInfo, string family)
{
if (!string.IsNullOrEmpty(family))
{
return new FontFamily(family);
}
else
{
return null;
}
}
internal static SystemColorHighlightingBrush GetSystemColorBrush(IXmlLineInfo lineInfo, string name)
internal static SystemColorHighlightingBrush GetSystemColorBrush(IXmlLineInfo lineInfo, string name)
{
Debug.Assert(name.StartsWith("SystemColors.", StringComparison.Ordinal));
string shortName = name.Substring(13);
@ -347,21 +373,21 @@ namespace ICSharpCode.AvalonEdit.Highlighting.Xshd
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))

Просмотреть файл

@ -36,31 +36,31 @@ namespace ICSharpCode.AvalonEdit.Highlighting.Xshd
/// </summary>
public string Name { get; set; }
/// <summary>
/// Gets/sets the font family
/// </summary>
public FontFamily FontFamily { get; set; }
/// <summary>
/// Gets/sets the font family
/// </summary>
public FontFamily FontFamily { get; set; }
/// <summary>
/// Gets/sets the font size.
/// </summary>
public int? FontSize { get; set; }
/// <summary>
/// Gets/sets the font size.
/// </summary>
public int? FontSize { get; set; }
/// <summary>
/// Gets/sets the foreground brush.
/// </summary>
public HighlightingBrush Foreground { get; set; }
/// <summary>
/// Gets/sets the foreground brush.
/// </summary>
public HighlightingBrush Foreground { get; set; }
/// <summary>
/// Gets/sets the background brush.
/// </summary>
public HighlightingBrush Background { get; set; }
/// <summary>
/// Gets/sets the font weight.
/// </summary>
public FontWeight? FontWeight { get; set; }
/// <summary>
/// Gets/sets the underline flag
/// </summary>
@ -75,19 +75,19 @@ namespace ICSharpCode.AvalonEdit.Highlighting.Xshd
/// Gets/sets the font style.
/// </summary>
public FontStyle? FontStyle { get; set; }
/// <summary>
/// Gets/Sets the example text that demonstrates where the color is used.
/// </summary>
public string ExampleText { get; set; }
/// <summary>
/// Creates a new XshdColor instance.
/// </summary>
public XshdColor()
{
}
/// <summary>
/// Deserializes an XshdColor.
/// </summary>
@ -108,11 +108,11 @@ namespace ICSharpCode.AvalonEdit.Highlighting.Xshd
if (info.GetBoolean("HasStrikethrough"))
this.Strikethrough = info.GetBoolean("Strikethrough");
if (info.GetBoolean("HasFamily"))
this.FontFamily = new FontFamily(info.GetString("Family"));
if (info.GetBoolean("HasSize"))
this.FontSize = info.GetInt32("Size");
this.FontFamily = new FontFamily(info.GetString("Family"));
if (info.GetBoolean("HasSize"))
this.FontSize = info.GetInt32("Size");
}
/// <summary>
/// Serializes this XshdColor instance.
/// </summary>
@ -138,16 +138,16 @@ namespace ICSharpCode.AvalonEdit.Highlighting.Xshd
if (this.FontStyle.HasValue)
info.AddValue("Style", this.FontStyle.Value.ToString());
info.AddValue("ExampleText", this.ExampleText);
info.AddValue("HasFamily", this.FontFamily != null);
if (this.FontFamily != null)
info.AddValue("Family", this.FontFamily.FamilyNames.FirstOrDefault());
info.AddValue("HasSize", this.FontSize.HasValue);
if (this.FontSize.HasValue)
info.AddValue("Size", this.FontSize.Value.ToString());
}
info.AddValue("HasFamily", this.FontFamily != null);
if (this.FontFamily != null)
info.AddValue("Family", this.FontFamily.FamilyNames.FirstOrDefault());
info.AddValue("HasSize", this.FontSize.HasValue);
if (this.FontSize.HasValue)
info.AddValue("Size", this.FontSize.Value.ToString());
}
/// <inheritdoc/>
public override object AcceptVisitor(IXshdVisitor visitor)
/// <inheritdoc/>
public override object AcceptVisitor(IXshdVisitor visitor)
{
return visitor.VisitColor(this);
}