30 changed files with 1169 additions and 656 deletions
			
			
		@ -0,0 +1,69 @@
				@@ -0,0 +1,69 @@
					 | 
				
			||||
// 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.IO; | 
				
			||||
using System.Text; | 
				
			||||
using ICSharpCode.NRefactory.Editor; | 
				
			||||
 | 
				
			||||
namespace ICSharpCode.AvalonEdit.Document | 
				
			||||
{ | 
				
			||||
	/// <summary>
 | 
				
			||||
	/// A TextWriter implementation that directly inserts into a document.
 | 
				
			||||
	/// </summary>
 | 
				
			||||
	public class DocumentTextWriter : TextWriter | 
				
			||||
	{ | 
				
			||||
		readonly IDocument document; | 
				
			||||
		int insertionOffset; | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Creates a new DocumentTextWriter that inserts into document, starting at insertionOffset.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		public DocumentTextWriter(IDocument document, int insertionOffset) | 
				
			||||
		{ | 
				
			||||
			this.insertionOffset = insertionOffset; | 
				
			||||
			if (document == null) | 
				
			||||
				throw new ArgumentNullException("document"); | 
				
			||||
			this.document = document; | 
				
			||||
			var line = document.GetLineByOffset(insertionOffset); | 
				
			||||
			if (line.DelimiterLength == 0) | 
				
			||||
				line = line.PreviousLine; | 
				
			||||
			if (line != null) | 
				
			||||
				this.NewLine = document.GetText(line.EndOffset, line.DelimiterLength); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Gets/Sets the current insertion offset.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		public int InsertionOffset { | 
				
			||||
			get { return insertionOffset; } | 
				
			||||
			set { insertionOffset = value; } | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		public override void Write(char value) | 
				
			||||
		{ | 
				
			||||
			document.Insert(insertionOffset, value.ToString()); | 
				
			||||
			insertionOffset++; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		public override void Write(char[] buffer, int index, int count) | 
				
			||||
		{ | 
				
			||||
			document.Insert(insertionOffset, new string(buffer, index, count)); | 
				
			||||
			insertionOffset += count; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		public override void Write(string value) | 
				
			||||
		{ | 
				
			||||
			document.Insert(insertionOffset, value); | 
				
			||||
			insertionOffset += value.Length; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		public override Encoding Encoding { | 
				
			||||
			get { return Encoding.UTF8; } | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
@ -0,0 +1,62 @@
				@@ -0,0 +1,62 @@
					 | 
				
			||||
// 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.IO; | 
				
			||||
using System.Net; | 
				
			||||
 | 
				
			||||
namespace ICSharpCode.AvalonEdit.Highlighting | 
				
			||||
{ | 
				
			||||
	/// <summary>
 | 
				
			||||
	/// Holds options for converting text to HTML.
 | 
				
			||||
	/// </summary>
 | 
				
			||||
	public class HtmlOptions | 
				
			||||
	{ | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Creates a default HtmlOptions instance.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		public HtmlOptions() | 
				
			||||
		{ | 
				
			||||
			this.TabSize = 4; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Creates a new HtmlOptions instance that copies applicable options from the <see cref="TextEditorOptions"/>.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		public HtmlOptions(TextEditorOptions options) : this() | 
				
			||||
		{ | 
				
			||||
			if (options == null) | 
				
			||||
				throw new ArgumentNullException("options"); | 
				
			||||
			this.TabSize = options.IndentationSize; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// The amount of spaces a tab gets converted to.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		public int TabSize { get; set; } | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Writes the HTML attribute for the style to the text writer.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		public virtual void WriteStyleAttributeForColor(TextWriter writer, HighlightingColor color) | 
				
			||||
		{ | 
				
			||||
			if (writer == null) | 
				
			||||
				throw new ArgumentNullException("writer"); | 
				
			||||
			if (color == null) | 
				
			||||
				throw new ArgumentNullException("color"); | 
				
			||||
			writer.Write(" style=\""); | 
				
			||||
			WebUtility.HtmlEncode(color.ToCss(), writer); | 
				
			||||
			writer.Write('"'); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Gets whether the color needs to be written out to HTML.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		public virtual bool ColorNeedsSpanForStyling(HighlightingColor color) | 
				
			||||
		{ | 
				
			||||
			if (color == null) | 
				
			||||
				throw new ArgumentNullException("color"); | 
				
			||||
			return !string.IsNullOrEmpty(color.ToCss()); | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
@ -0,0 +1,236 @@
				@@ -0,0 +1,236 @@
					 | 
				
			||||
// 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.IO; | 
				
			||||
using System.Net; | 
				
			||||
using System.Text; | 
				
			||||
using System.Windows; | 
				
			||||
using System.Windows.Media; | 
				
			||||
using ICSharpCode.AvalonEdit.Utils; | 
				
			||||
 | 
				
			||||
namespace ICSharpCode.AvalonEdit.Highlighting | 
				
			||||
{ | 
				
			||||
	/// <summary>
 | 
				
			||||
	/// RichTextWriter implementation that produces HTML.
 | 
				
			||||
	/// </summary>
 | 
				
			||||
	public class HtmlRichTextWriter : RichTextWriter | 
				
			||||
	{ | 
				
			||||
		readonly TextWriter htmlWriter; | 
				
			||||
		readonly HtmlOptions options; | 
				
			||||
		Stack<string> endTagStack = new Stack<string>(); | 
				
			||||
		bool spaceNeedsEscaping = true; | 
				
			||||
		bool hasSpace; | 
				
			||||
		bool needIndentation = true; | 
				
			||||
		int indentationLevel; | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Creates a new HtmlRichTextWriter instance.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		/// <param name="htmlWriter">
 | 
				
			||||
		/// The text writer where the raw HTML is written to.
 | 
				
			||||
		/// The HtmlRichTextWriter does not take ownership of the htmlWriter;
 | 
				
			||||
		/// disposing the HtmlRichTextWriter will not dispose the underlying htmlWriter!
 | 
				
			||||
		/// </param>
 | 
				
			||||
		/// <param name="options">Options that control the HTML output.</param>
 | 
				
			||||
		public HtmlRichTextWriter(TextWriter htmlWriter, HtmlOptions options = null) | 
				
			||||
		{ | 
				
			||||
			if (htmlWriter == null) | 
				
			||||
				throw new ArgumentNullException("htmlWriter"); | 
				
			||||
			this.htmlWriter = htmlWriter; | 
				
			||||
			this.options = options ?? new HtmlOptions(); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		public override Encoding Encoding { | 
				
			||||
			get { return htmlWriter.Encoding; } | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		public override void Flush() | 
				
			||||
		{ | 
				
			||||
			FlushSpace(true); // next char potentially might be whitespace
 | 
				
			||||
			htmlWriter.Flush(); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		protected override void Dispose(bool disposing) | 
				
			||||
		{ | 
				
			||||
			if (disposing) { | 
				
			||||
				FlushSpace(true); | 
				
			||||
			} | 
				
			||||
			base.Dispose(disposing); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		void FlushSpace(bool nextIsWhitespace) | 
				
			||||
		{ | 
				
			||||
			if (hasSpace) { | 
				
			||||
				if (spaceNeedsEscaping || nextIsWhitespace) | 
				
			||||
					htmlWriter.Write(" "); | 
				
			||||
				else | 
				
			||||
					htmlWriter.Write(' '); | 
				
			||||
				hasSpace = false; | 
				
			||||
				spaceNeedsEscaping = true; | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		void WriteIndentation() | 
				
			||||
		{ | 
				
			||||
			if (needIndentation) { | 
				
			||||
				for (int i = 0; i < indentationLevel; i++) { | 
				
			||||
					WriteChar('\t'); | 
				
			||||
				} | 
				
			||||
				needIndentation = false; | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		public override void Write(char value) | 
				
			||||
		{ | 
				
			||||
			WriteIndentation(); | 
				
			||||
			WriteChar(value); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		static readonly char[] specialChars = { ' ', '\t', '\r', '\n' }; | 
				
			||||
		 | 
				
			||||
		void WriteChar(char c) | 
				
			||||
		{ | 
				
			||||
			bool isWhitespace = char.IsWhiteSpace(c); | 
				
			||||
			FlushSpace(isWhitespace); | 
				
			||||
			switch (c) { | 
				
			||||
				case ' ': | 
				
			||||
					if (spaceNeedsEscaping) | 
				
			||||
						htmlWriter.Write(" "); | 
				
			||||
					else | 
				
			||||
						hasSpace = true; | 
				
			||||
					break; | 
				
			||||
				case '\t': | 
				
			||||
					for (int i = 0; i < options.TabSize; i++) { | 
				
			||||
						htmlWriter.Write(" "); | 
				
			||||
					} | 
				
			||||
					break; | 
				
			||||
				case '\r': | 
				
			||||
					break; // ignore; we'll write the <br/> with the following \n
 | 
				
			||||
				case '\n': | 
				
			||||
					htmlWriter.Write("<br/>"); | 
				
			||||
					needIndentation = true; | 
				
			||||
					break; | 
				
			||||
				default: | 
				
			||||
					WebUtility.HtmlEncode(c.ToString(), htmlWriter); | 
				
			||||
					break; | 
				
			||||
			} | 
				
			||||
			// If we just handled a space by setting hasSpace = true,
 | 
				
			||||
			// we mustn't set spaceNeedsEscaping as doing so would affect our own space,
 | 
				
			||||
			// not just the following spaces.
 | 
				
			||||
			if (c != ' ') { | 
				
			||||
				// Following spaces must be escaped if c was a newline/tab;
 | 
				
			||||
				// and they don't need escaping if c was a normal character.
 | 
				
			||||
				spaceNeedsEscaping = isWhitespace; | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		public override void Write(string value) | 
				
			||||
		{ | 
				
			||||
			int pos = 0; | 
				
			||||
			do { | 
				
			||||
				int endPos = value.IndexOfAny(specialChars, pos); | 
				
			||||
				if (endPos < 0) { | 
				
			||||
					WriteSimpleString(value.Substring(pos)); | 
				
			||||
					return; // reached end of string
 | 
				
			||||
				} | 
				
			||||
				if (endPos > pos) | 
				
			||||
					WriteSimpleString(value.Substring(pos, endPos - pos)); | 
				
			||||
				WriteChar(value[pos]); | 
				
			||||
				pos = endPos + 1; | 
				
			||||
			} while (pos < value.Length); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		void WriteIndentationAndSpace() | 
				
			||||
		{ | 
				
			||||
			WriteIndentation(); | 
				
			||||
			FlushSpace(false); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		void WriteSimpleString(string value) | 
				
			||||
		{ | 
				
			||||
			if (value.Length == 0) | 
				
			||||
				return; | 
				
			||||
			WriteIndentationAndSpace(); | 
				
			||||
			WebUtility.HtmlEncode(value, htmlWriter); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		public override void Indent() | 
				
			||||
		{ | 
				
			||||
			indentationLevel++; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		public override void Unindent() | 
				
			||||
		{ | 
				
			||||
			if (indentationLevel == 0) | 
				
			||||
				throw new NotSupportedException(); | 
				
			||||
			indentationLevel--; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		protected override void BeginUnhandledSpan() | 
				
			||||
		{ | 
				
			||||
			endTagStack.Push(null); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		public override void EndSpan() | 
				
			||||
		{ | 
				
			||||
			htmlWriter.Write(endTagStack.Pop()); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		public override void BeginSpan(Color foregroundColor) | 
				
			||||
		{ | 
				
			||||
			BeginSpan(new HighlightingColor { Foreground = new SimpleHighlightingBrush(foregroundColor) }); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		public override void BeginSpan(FontFamily fontFamily) | 
				
			||||
		{ | 
				
			||||
			BeginUnhandledSpan(); // TODO
 | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		public override void BeginSpan(FontStyle fontStyle) | 
				
			||||
		{ | 
				
			||||
			BeginSpan(new HighlightingColor { FontStyle = fontStyle }); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		public override void BeginSpan(FontWeight fontWeight) | 
				
			||||
		{ | 
				
			||||
			BeginSpan(new HighlightingColor { FontWeight = fontWeight }); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		public override void BeginSpan(HighlightingColor highlightingColor) | 
				
			||||
		{ | 
				
			||||
			WriteIndentationAndSpace(); | 
				
			||||
			if (options.ColorNeedsSpanForStyling(highlightingColor)) { | 
				
			||||
				htmlWriter.Write("<span"); | 
				
			||||
				options.WriteStyleAttributeForColor(htmlWriter, highlightingColor); | 
				
			||||
				htmlWriter.Write('>'); | 
				
			||||
				endTagStack.Push("</span>"); | 
				
			||||
			} else { | 
				
			||||
				endTagStack.Push(null); | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		public override void BeginHyperlinkSpan(Uri uri) | 
				
			||||
		{ | 
				
			||||
			WriteIndentationAndSpace(); | 
				
			||||
			htmlWriter.Write("<a href=\"" + WebUtility.HtmlEncode(uri.ToString()) + "\">"); | 
				
			||||
			endTagStack.Push("</a>"); | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
@ -0,0 +1,39 @@
				@@ -0,0 +1,39 @@
					 | 
				
			||||
// 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 ICSharpCode.AvalonEdit.Document; | 
				
			||||
using ICSharpCode.AvalonEdit.Rendering; | 
				
			||||
 | 
				
			||||
namespace ICSharpCode.AvalonEdit.Highlighting | 
				
			||||
{ | 
				
			||||
	/// <summary>
 | 
				
			||||
	/// A colorizer that applies the highlighting from a <see cref="RichTextModel"/> to the editor.
 | 
				
			||||
	/// </summary>
 | 
				
			||||
	public class RichTextColorizer : DocumentColorizingTransformer | 
				
			||||
	{ | 
				
			||||
		readonly RichTextModel richTextModel; | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Creates a new RichTextColorizer instance.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		public RichTextColorizer(RichTextModel richTextModel) | 
				
			||||
		{ | 
				
			||||
			if (richTextModel == null) | 
				
			||||
				throw new ArgumentNullException("richTextModel"); | 
				
			||||
			this.richTextModel = richTextModel; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		protected override void ColorizeLine(DocumentLine line) | 
				
			||||
		{ | 
				
			||||
			var sections = richTextModel.GetHighlightedSections(line.Offset, line.Length); | 
				
			||||
			foreach (HighlightedSection section in sections) { | 
				
			||||
				if (HighlightingColorizer.IsEmptyColor(section.Color)) | 
				
			||||
					continue; | 
				
			||||
				ChangeLinePart(section.Offset, section.Offset + section.Length, | 
				
			||||
				               visualLineElement => HighlightingColorizer.ApplyColorToElement(visualLineElement, section.Color, CurrentContext)); | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
@ -0,0 +1,165 @@
				@@ -0,0 +1,165 @@
					 | 
				
			||||
// 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; | 
				
			||||
using System.Collections.Generic; | 
				
			||||
using ICSharpCode.NRefactory.Editor; | 
				
			||||
using ICSharpCode.NRefactory.TypeSystem.Implementation; | 
				
			||||
using ICSharpCode.AvalonEdit.Document; | 
				
			||||
using ICSharpCode.AvalonEdit.Utils; | 
				
			||||
 | 
				
			||||
namespace ICSharpCode.AvalonEdit.Highlighting | 
				
			||||
{ | 
				
			||||
	/// <summary>
 | 
				
			||||
	/// Stores rich-text formatting.
 | 
				
			||||
	/// </summary>
 | 
				
			||||
	public sealed class RichTextModel | 
				
			||||
	{ | 
				
			||||
		CompressingTreeList<HighlightingColor> list = new CompressingTreeList<HighlightingColor>(object.Equals); | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Gets the length of the document.
 | 
				
			||||
		/// This has an effect on which coordinates are valid for this RichTextModel.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		public int DocumentLength { | 
				
			||||
			get { return list.Count; } | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Creates a new RichTextModel that needs manual calls to <see cref="UpdateOffsets(DocumentChangeEventArgs)"/>.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		public RichTextModel(int documentLength) | 
				
			||||
		{ | 
				
			||||
			list.InsertRange(0, documentLength, HighlightingColor.Empty); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		#region UpdateOffsets
 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Updates the start and end offsets of all segments stored in this collection.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		/// <param name="e">DocumentChangeEventArgs instance describing the change to the document.</param>
 | 
				
			||||
		public void UpdateOffsets(DocumentChangeEventArgs e) | 
				
			||||
		{ | 
				
			||||
			if (e == null) | 
				
			||||
				throw new ArgumentNullException("e"); | 
				
			||||
			OffsetChangeMap map = e.OffsetChangeMapOrNull; | 
				
			||||
			if (map != null) { | 
				
			||||
				foreach (OffsetChangeMapEntry entry in map) { | 
				
			||||
					UpdateOffsetsInternal(entry); | 
				
			||||
				} | 
				
			||||
			} else { | 
				
			||||
				UpdateOffsetsInternal(e.CreateSingleChangeMapEntry()); | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Updates the start and end offsets of all segments stored in this collection.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		/// <param name="change">OffsetChangeMapEntry instance describing the change to the document.</param>
 | 
				
			||||
		public void UpdateOffsets(OffsetChangeMapEntry change) | 
				
			||||
		{ | 
				
			||||
			UpdateOffsetsInternal(change); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		void UpdateOffsetsInternal(OffsetChangeMapEntry entry) | 
				
			||||
		{ | 
				
			||||
			HighlightingColor color; | 
				
			||||
			if (entry.RemovalLength > 0) { | 
				
			||||
				color = list[entry.Offset]; | 
				
			||||
				list.RemoveRange(entry.Offset, entry.RemovalLength); | 
				
			||||
			} else if (list.Count > 0) { | 
				
			||||
				color = list[Math.Max(0, entry.Offset - 1)]; | 
				
			||||
			} else { | 
				
			||||
				color = HighlightingColor.Empty; | 
				
			||||
			} | 
				
			||||
			list.InsertRange(entry.Offset, entry.InsertionLength, color); | 
				
			||||
		} | 
				
			||||
		#endregion
 | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Gets the HighlightingColor for the specified offset.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		public HighlightingColor GetHighlighting(int offset) | 
				
			||||
		{ | 
				
			||||
			return list[offset]; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Applies the HighlightingColor to the specified range of text.
 | 
				
			||||
		/// If the color specifies <c>null</c> for some properties, existing highlighting is preserved.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		public void ApplyHighlighting(int offset, int length, HighlightingColor color) | 
				
			||||
		{ | 
				
			||||
			list.TransformRange(offset, length, c => { | 
				
			||||
			                    	var newColor = c.Clone(); | 
				
			||||
			                    	newColor.MergeWith(color); | 
				
			||||
			                    	newColor.Freeze(); | 
				
			||||
			                    	return newColor; | 
				
			||||
			                    }); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Sets the HighlightingColor for the specified range of text,
 | 
				
			||||
		/// completely replacing the existing highlighting in that area.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		public void SetHighlighting(int offset, int length, HighlightingColor color) | 
				
			||||
		{ | 
				
			||||
			list.SetRange(offset, length, FreezableHelper.GetFrozenClone(color)); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Retrieves the highlighted sections in the specified range.
 | 
				
			||||
		/// The highlighted sections will be sorted by offset, and there will not be any nested or overlapping sections.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		public IEnumerable<HighlightedSection> GetHighlightedSections(int offset, int length) | 
				
			||||
		{ | 
				
			||||
			int pos = offset; | 
				
			||||
			int endOffset = offset + length; | 
				
			||||
			while (pos < endOffset) { | 
				
			||||
				int endPos = Math.Min(endOffset, list.GetEndOfRun(pos)); | 
				
			||||
				yield return new HighlightedSection { | 
				
			||||
					Offset = pos, | 
				
			||||
					Length = endPos - pos, | 
				
			||||
					Color = list[pos] | 
				
			||||
				}; | 
				
			||||
				pos = endPos; | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		#region WriteDocumentTo
 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Writes the specified document, with the formatting from this rich text model applied,
 | 
				
			||||
		/// to the RichTextWriter.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		public void WriteDocumentTo(ITextSource document, RichTextWriter writer) | 
				
			||||
		{ | 
				
			||||
			WriteDocumentTo(document, new SimpleSegment(0, DocumentLength), writer); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Writes a segment of the specified document, with the formatting from this rich text model applied,
 | 
				
			||||
		/// to the RichTextWriter.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		public void WriteDocumentTo(ITextSource document, ISegment segment, RichTextWriter writer) | 
				
			||||
		{ | 
				
			||||
			if (document == null) | 
				
			||||
				throw new ArgumentNullException("document"); | 
				
			||||
			if (segment == null) | 
				
			||||
				throw new ArgumentNullException("segment"); | 
				
			||||
			if (writer == null) | 
				
			||||
				throw new ArgumentNullException("writer"); | 
				
			||||
			 | 
				
			||||
			int pos = segment.Offset; | 
				
			||||
			int endOffset = segment.EndOffset; | 
				
			||||
			while (pos < endOffset) { | 
				
			||||
				int endPos = Math.Min(endOffset, list.GetEndOfRun(pos)); | 
				
			||||
				writer.BeginSpan(list[pos]); | 
				
			||||
				document.WriteTextTo(writer, pos, endPos - pos); | 
				
			||||
				writer.EndSpan(); | 
				
			||||
				pos = endPos; | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		#endregion
 | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
@ -0,0 +1,118 @@
				@@ -0,0 +1,118 @@
					 | 
				
			||||
// 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.IO; | 
				
			||||
using System.Windows; | 
				
			||||
using System.Windows.Media; | 
				
			||||
using ICSharpCode.NRefactory.Editor; | 
				
			||||
using ICSharpCode.AvalonEdit.Document; | 
				
			||||
using ICSharpCode.AvalonEdit.Utils; | 
				
			||||
 | 
				
			||||
namespace ICSharpCode.AvalonEdit.Highlighting | 
				
			||||
{ | 
				
			||||
	/// <summary>
 | 
				
			||||
	/// A RichTextWriter that writes into a document and .
 | 
				
			||||
	/// </summary>
 | 
				
			||||
	public class RichTextModelWriter : PlainRichTextWriter | 
				
			||||
	{ | 
				
			||||
		readonly RichTextModel richTextModel; | 
				
			||||
		readonly DocumentTextWriter documentTextWriter; | 
				
			||||
		readonly Stack<HighlightingColor> colorStack = new Stack<HighlightingColor>(); | 
				
			||||
		HighlightingColor currentColor; | 
				
			||||
		int currentColorBegin = -1; | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Creates a new RichTextModelWriter that inserts into document, starting at insertionOffset.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		public RichTextModelWriter(RichTextModel richTextModel, IDocument document, int insertionOffset) | 
				
			||||
			: base(new DocumentTextWriter(document, insertionOffset)) | 
				
			||||
		{ | 
				
			||||
			if (richTextModel == null) | 
				
			||||
				throw new ArgumentNullException("richTextModel"); | 
				
			||||
			this.richTextModel = richTextModel; | 
				
			||||
			this.documentTextWriter = (DocumentTextWriter)base.textWriter; | 
				
			||||
			if (richTextModel.DocumentLength == 0) | 
				
			||||
				currentColor = HighlightingColor.Empty; | 
				
			||||
			else | 
				
			||||
				currentColor = richTextModel.GetHighlighting(Math.Max(0, insertionOffset - 1)); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Gets/Sets the current insertion offset.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		public int InsertionOffset { | 
				
			||||
			get { return documentTextWriter.InsertionOffset; } | 
				
			||||
			set { documentTextWriter.InsertionOffset = value; } | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		protected override void BeginUnhandledSpan() | 
				
			||||
		{ | 
				
			||||
			colorStack.Push(currentColor); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		void BeginColorSpan() | 
				
			||||
		{ | 
				
			||||
			WriteIndentationIfNecessary(); | 
				
			||||
			colorStack.Push(currentColor); | 
				
			||||
			currentColor = currentColor.Clone(); | 
				
			||||
			currentColorBegin = documentTextWriter.InsertionOffset; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		public override void EndSpan() | 
				
			||||
		{ | 
				
			||||
			currentColor = colorStack.Pop(); | 
				
			||||
			currentColorBegin = documentTextWriter.InsertionOffset; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		protected override void AfterWrite() | 
				
			||||
		{ | 
				
			||||
			base.AfterWrite(); | 
				
			||||
			richTextModel.SetHighlighting(currentColorBegin, documentTextWriter.InsertionOffset - currentColorBegin, currentColor); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		public override void BeginSpan(Color foregroundColor) | 
				
			||||
		{ | 
				
			||||
			BeginColorSpan(); | 
				
			||||
			currentColor.Foreground = new SimpleHighlightingBrush(foregroundColor); | 
				
			||||
			currentColor.Freeze(); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		public override void BeginSpan(FontFamily fontFamily) | 
				
			||||
		{ | 
				
			||||
			BeginUnhandledSpan(); // TODO
 | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		public override void BeginSpan(FontStyle fontStyle) | 
				
			||||
		{ | 
				
			||||
			BeginColorSpan(); | 
				
			||||
			currentColor.FontStyle = fontStyle; | 
				
			||||
			currentColor.Freeze(); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		public override void BeginSpan(FontWeight fontWeight) | 
				
			||||
		{ | 
				
			||||
			BeginColorSpan(); | 
				
			||||
			currentColor.FontWeight = fontWeight; | 
				
			||||
			currentColor.Freeze(); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		public override void BeginSpan(HighlightingColor highlightingColor) | 
				
			||||
		{ | 
				
			||||
			BeginColorSpan(); | 
				
			||||
			currentColor.MergeWith(highlightingColor); | 
				
			||||
			currentColor.Freeze(); | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
@ -0,0 +1,125 @@
				@@ -0,0 +1,125 @@
					 | 
				
			||||
// 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.IO; | 
				
			||||
using System.Text; | 
				
			||||
 | 
				
			||||
namespace ICSharpCode.AvalonEdit.Utils | 
				
			||||
{ | 
				
			||||
	/// <summary>
 | 
				
			||||
	/// RichTextWriter implementation that writes plain text only
 | 
				
			||||
	/// and ignores all formatted spans.
 | 
				
			||||
	/// </summary>
 | 
				
			||||
	public class PlainRichTextWriter : RichTextWriter | 
				
			||||
	{ | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// The text writer that was passed to the PlainRichTextWriter constructor.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		protected readonly TextWriter textWriter; | 
				
			||||
		string indentationString = "\t"; | 
				
			||||
		int indentationLevel; | 
				
			||||
		char prevChar; | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Creates a new PlainRichTextWriter instance that writes the text to the specified text writer.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		public PlainRichTextWriter(TextWriter textWriter) | 
				
			||||
		{ | 
				
			||||
			if (textWriter == null) | 
				
			||||
				throw new ArgumentNullException("textWriter"); | 
				
			||||
			this.textWriter = textWriter; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Gets/Sets the string used to indent by one level.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		public string IndentationString { | 
				
			||||
			get { | 
				
			||||
				return indentationString; | 
				
			||||
			} | 
				
			||||
			set { | 
				
			||||
				indentationString = value; | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		protected override void BeginUnhandledSpan() | 
				
			||||
		{ | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		public override void EndSpan() | 
				
			||||
		{ | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		void WriteIndentation() | 
				
			||||
		{ | 
				
			||||
			for (int i = 0; i < indentationLevel; i++) { | 
				
			||||
				textWriter.Write(indentationString); | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Writes the indentation, if necessary.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		protected void WriteIndentationIfNecessary() | 
				
			||||
		{ | 
				
			||||
			if (prevChar == '\n') { | 
				
			||||
				WriteIndentation(); | 
				
			||||
				prevChar = '\0'; | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Is called after a write operation.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		protected virtual void AfterWrite() | 
				
			||||
		{ | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		public override void Write(char value) | 
				
			||||
		{ | 
				
			||||
			if (prevChar == '\n') | 
				
			||||
				WriteIndentation(); | 
				
			||||
			textWriter.Write(value); | 
				
			||||
			prevChar = value; | 
				
			||||
			AfterWrite(); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		public override void Indent() | 
				
			||||
		{ | 
				
			||||
			indentationLevel++; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		public override void Unindent() | 
				
			||||
		{ | 
				
			||||
			if (indentationLevel == 0) | 
				
			||||
				throw new NotSupportedException(); | 
				
			||||
			indentationLevel--; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		public override Encoding Encoding { | 
				
			||||
			get { return textWriter.Encoding; } | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		public override IFormatProvider FormatProvider { | 
				
			||||
			get { return textWriter.FormatProvider; } | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <inheritdoc/>
 | 
				
			||||
		public override string NewLine { | 
				
			||||
			get { | 
				
			||||
				return textWriter.NewLine; | 
				
			||||
			} | 
				
			||||
			set { | 
				
			||||
				textWriter.NewLine = value; | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
@ -0,0 +1,85 @@
				@@ -0,0 +1,85 @@
					 | 
				
			||||
// 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.IO; | 
				
			||||
using System.Windows; | 
				
			||||
using System.Windows.Media; | 
				
			||||
 | 
				
			||||
namespace ICSharpCode.AvalonEdit.Utils | 
				
			||||
{ | 
				
			||||
	/// <summary>
 | 
				
			||||
	/// A text writer that supports creating spans of highlighted text.
 | 
				
			||||
	/// </summary>
 | 
				
			||||
	public abstract class RichTextWriter : TextWriter | 
				
			||||
	{ | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Gets called by the RichTextWriter base class when a BeginSpan() method
 | 
				
			||||
		/// that is not overwritten gets called.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		protected abstract void BeginUnhandledSpan(); | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Begin a colored span.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		public virtual void BeginSpan(Color foregroundColor) | 
				
			||||
		{ | 
				
			||||
			BeginUnhandledSpan(); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Begin a span with modified font weight.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		public virtual void BeginSpan(FontWeight fontWeight) | 
				
			||||
		{ | 
				
			||||
			BeginUnhandledSpan(); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Begin a span with modified font style.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		public virtual void BeginSpan(FontStyle fontStyle) | 
				
			||||
		{ | 
				
			||||
			BeginUnhandledSpan(); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Begin a span with modified font family.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		public virtual void BeginSpan(FontFamily fontFamily) | 
				
			||||
		{ | 
				
			||||
			BeginUnhandledSpan(); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Begin a highlighted span.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		public virtual void BeginSpan(Highlighting.HighlightingColor highlightingColor) | 
				
			||||
		{ | 
				
			||||
			BeginUnhandledSpan(); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Begin a span that links to the specified URI.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		public virtual void BeginHyperlinkSpan(Uri uri) | 
				
			||||
		{ | 
				
			||||
			BeginUnhandledSpan(); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Marks the end of the current span.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		public abstract void EndSpan(); | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Increases the indentation level.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		public abstract void Indent(); | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Decreases the indentation level.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		public abstract void Unindent(); | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
@ -1,439 +0,0 @@
				@@ -1,439 +0,0 @@
					 | 
				
			||||
// 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.Linq; | 
				
			||||
using System.Text; | 
				
			||||
using ICSharpCode.AvalonEdit.Utils; | 
				
			||||
using ICSharpCode.NRefactory.Utils; | 
				
			||||
 | 
				
			||||
namespace ICSharpCode.AvalonEdit.Xml | 
				
			||||
{ | 
				
			||||
	class TagMatchingHeuristics | 
				
			||||
	{ | 
				
			||||
		const int maxConfigurationCount = 10; | 
				
			||||
		 | 
				
			||||
		AXmlParser parser; | 
				
			||||
		TrackedSegmentCollection trackedSegments; | 
				
			||||
		string input; | 
				
			||||
		List<AXmlObject> tags; | 
				
			||||
		 | 
				
			||||
		public TagMatchingHeuristics(AXmlParser parser, string input, List<AXmlObject> tags) | 
				
			||||
		{ | 
				
			||||
			this.parser = parser; | 
				
			||||
			this.trackedSegments = parser.TrackedSegments; | 
				
			||||
			this.input = input; | 
				
			||||
			this.tags = tags; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		public AXmlDocument ReadDocument() | 
				
			||||
		{ | 
				
			||||
			AXmlDocument doc = new AXmlDocument() { Parser = parser }; | 
				
			||||
			 | 
				
			||||
			// AXmlParser.Log("Flat stream: {0}", PrintObjects(tags));
 | 
				
			||||
			List<AXmlObject> valid = MatchTags(tags); | 
				
			||||
			// AXmlParser.Log("Fixed stream: {0}", PrintObjects(valid));
 | 
				
			||||
			IEnumerator<AXmlObject> validStream = valid.GetEnumerator(); | 
				
			||||
			validStream.MoveNext(); // Move to first
 | 
				
			||||
			while(true) { | 
				
			||||
				// End of stream?
 | 
				
			||||
				try { | 
				
			||||
					if (validStream.Current == null) break; | 
				
			||||
				} catch (InvalidCastException) { | 
				
			||||
					break; | 
				
			||||
				} | 
				
			||||
				doc.AddChild(ReadTextOrElement(validStream)); | 
				
			||||
			} | 
				
			||||
			 | 
				
			||||
			if (doc.Children.Count > 0) { | 
				
			||||
				doc.StartOffset = doc.FirstChild.StartOffset; | 
				
			||||
				doc.EndOffset = doc.LastChild.EndOffset; | 
				
			||||
			} | 
				
			||||
			 | 
				
			||||
			// Check well formed
 | 
				
			||||
			foreach(AXmlTag xmlDeclaration in doc.Children.OfType<AXmlTag>().Where(t => t.IsProcessingInstruction && string.Equals(t.Name, "xml", StringComparison.OrdinalIgnoreCase))) { | 
				
			||||
				if (xmlDeclaration.StartOffset != 0) | 
				
			||||
					TagReader.OnSyntaxError(doc, xmlDeclaration.StartOffset, xmlDeclaration.StartOffset + 5, | 
				
			||||
					                        "XML declaration must be at the start of document"); | 
				
			||||
			} | 
				
			||||
			int elemCount = doc.Children.OfType<AXmlElement>().Count(); | 
				
			||||
			if (elemCount == 0) | 
				
			||||
				TagReader.OnSyntaxError(doc, doc.EndOffset, doc.EndOffset, | 
				
			||||
				                        "Root element is missing"); | 
				
			||||
			if (elemCount > 1) { | 
				
			||||
				AXmlElement next = doc.Children.OfType<AXmlElement>().Skip(1).First(); | 
				
			||||
				TagReader.OnSyntaxError(doc, next.StartOffset, next.StartOffset, | 
				
			||||
				                        "Only one root element is allowed"); | 
				
			||||
			} | 
				
			||||
			foreach(AXmlTag tag in doc.Children.OfType<AXmlTag>()) { | 
				
			||||
				if (tag.IsCData) | 
				
			||||
					TagReader.OnSyntaxError(doc, tag.StartOffset, tag.EndOffset, | 
				
			||||
					                        "CDATA not allowed in document root"); | 
				
			||||
			} | 
				
			||||
			foreach(AXmlText text in doc.Children.OfType<AXmlText>()) { | 
				
			||||
				if (!text.ContainsOnlyWhitespace) | 
				
			||||
					TagReader.OnSyntaxError(doc, text.StartOffset, text.EndOffset, | 
				
			||||
					                        "Only whitespace is allowed in document root"); | 
				
			||||
			} | 
				
			||||
				 | 
				
			||||
			 | 
				
			||||
			AXmlParser.Log("Constructed {0}", doc); | 
				
			||||
			trackedSegments.AddParsedObject(doc, null); | 
				
			||||
			return doc; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		static AXmlObject ReadSingleObject(IEnumerator<AXmlObject> objStream) | 
				
			||||
		{ | 
				
			||||
			AXmlObject obj = objStream.Current; | 
				
			||||
			objStream.MoveNext(); | 
				
			||||
			return obj; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		AXmlObject ReadTextOrElement(IEnumerator<AXmlObject> objStream) | 
				
			||||
		{ | 
				
			||||
			AXmlObject curr = objStream.Current; | 
				
			||||
			if (curr is AXmlText || curr is AXmlElement) { | 
				
			||||
				return ReadSingleObject(objStream); | 
				
			||||
			} else { | 
				
			||||
				AXmlTag currTag = (AXmlTag)curr; | 
				
			||||
				if (currTag == StartTagPlaceholder) { | 
				
			||||
					return ReadElement(objStream); | 
				
			||||
				} else if (currTag.IsStartOrEmptyTag) { | 
				
			||||
					return ReadElement(objStream); | 
				
			||||
				} else { | 
				
			||||
					return ReadSingleObject(objStream); | 
				
			||||
				} | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		AXmlElement ReadElement(IEnumerator<AXmlObject> objStream) | 
				
			||||
		{ | 
				
			||||
			AXmlElement element = new AXmlElement(); | 
				
			||||
			element.IsProperlyNested = true; | 
				
			||||
			 | 
				
			||||
			// Read start tag
 | 
				
			||||
			AXmlTag startTag = ReadSingleObject(objStream) as AXmlTag; | 
				
			||||
			AXmlParser.DebugAssert(startTag != null, "Start tag expected"); | 
				
			||||
			AXmlParser.DebugAssert(startTag.IsStartOrEmptyTag || startTag == StartTagPlaceholder, "Start tag expected"); | 
				
			||||
			if (startTag == StartTagPlaceholder) { | 
				
			||||
				element.HasStartOrEmptyTag = false; | 
				
			||||
				element.IsProperlyNested = false; | 
				
			||||
				TagReader.OnSyntaxError(element, objStream.Current.StartOffset, objStream.Current.EndOffset, | 
				
			||||
				                        "Matching openning tag was not found"); | 
				
			||||
			} else { | 
				
			||||
				element.HasStartOrEmptyTag = true; | 
				
			||||
				element.AddChild(startTag); | 
				
			||||
			} | 
				
			||||
			 | 
				
			||||
			// Read content and end tag
 | 
				
			||||
			if (startTag == StartTagPlaceholder || // Check first in case the start tag is null
 | 
				
			||||
			    element.StartTag.IsStartTag) | 
				
			||||
			{ | 
				
			||||
				while(true) { | 
				
			||||
					AXmlTag currTag = objStream.Current as AXmlTag; // Peek
 | 
				
			||||
					if (currTag == EndTagPlaceholder) { | 
				
			||||
						TagReader.OnSyntaxError(element, element.LastChild.EndOffset, element.LastChild.EndOffset, | 
				
			||||
						                        "Expected '</{0}>'", element.StartTag.Name); | 
				
			||||
						ReadSingleObject(objStream); | 
				
			||||
						element.HasEndTag = false; | 
				
			||||
						element.IsProperlyNested = false; | 
				
			||||
						break; | 
				
			||||
					} else if (currTag != null && currTag.IsEndTag) { | 
				
			||||
						if (element.HasStartOrEmptyTag && currTag.Name != element.StartTag.Name) { | 
				
			||||
							TagReader.OnSyntaxError(element, currTag.StartOffset + 2, currTag.StartOffset + 2 + currTag.Name.Length, | 
				
			||||
							                        "Expected '{0}'.  End tag must have same name as start tag.", element.StartTag.Name); | 
				
			||||
						} | 
				
			||||
						element.AddChild(ReadSingleObject(objStream)); | 
				
			||||
						element.HasEndTag = true; | 
				
			||||
						break; | 
				
			||||
					} | 
				
			||||
					AXmlObject nested = ReadTextOrElement(objStream); | 
				
			||||
					 | 
				
			||||
					AXmlElement nestedAsElement = nested as AXmlElement; | 
				
			||||
					if (nestedAsElement != null) { | 
				
			||||
						if (!nestedAsElement.IsProperlyNested) | 
				
			||||
							element.IsProperlyNested = false; | 
				
			||||
						element.AddChildren(Split(nestedAsElement).ToList()); | 
				
			||||
					} else { | 
				
			||||
						element.AddChild(nested); | 
				
			||||
					} | 
				
			||||
				} | 
				
			||||
			} else { | 
				
			||||
				element.HasEndTag = false; | 
				
			||||
			} | 
				
			||||
			 | 
				
			||||
			element.StartOffset = element.FirstChild.StartOffset; | 
				
			||||
			element.EndOffset = element.LastChild.EndOffset; | 
				
			||||
			 | 
				
			||||
			AXmlParser.Assert(element.HasStartOrEmptyTag || element.HasEndTag, "Must have at least start or end tag"); | 
				
			||||
			 | 
				
			||||
			AXmlParser.Log("Constructed {0}", element); | 
				
			||||
			trackedSegments.AddParsedObject(element, null); // Need all elements in cache for offset tracking
 | 
				
			||||
			return element; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		IEnumerable<AXmlObject> Split(AXmlElement elem) | 
				
			||||
		{ | 
				
			||||
			int myIndention = GetIndentLevel(elem); | 
				
			||||
			// Has start tag and no end tag ?  (other then empty-element tag)
 | 
				
			||||
			if (elem.HasStartOrEmptyTag && elem.StartTag.IsStartTag && !elem.HasEndTag && myIndention != -1) { | 
				
			||||
				int lastAccepted = 0; // Accept start tag
 | 
				
			||||
				while (lastAccepted + 1 < elem.Children.Count) { | 
				
			||||
					AXmlObject nextItem = elem.Children[lastAccepted + 1]; | 
				
			||||
					if (nextItem is AXmlText) { | 
				
			||||
						lastAccepted++; continue;  // Accept
 | 
				
			||||
					} else { | 
				
			||||
						// Include all more indented items
 | 
				
			||||
						if (GetIndentLevel(nextItem) > myIndention) { | 
				
			||||
							lastAccepted++; continue;  // Accept
 | 
				
			||||
						} else { | 
				
			||||
							break;  // Reject
 | 
				
			||||
						} | 
				
			||||
					} | 
				
			||||
				} | 
				
			||||
				// Accepted everything?
 | 
				
			||||
				if (lastAccepted + 1 == elem.Children.Count) { | 
				
			||||
					yield return elem; | 
				
			||||
					yield break; | 
				
			||||
				} | 
				
			||||
				AXmlParser.Log("Splitting {0} - take {1} of {2} nested", elem, lastAccepted, elem.Children.Count - 1); | 
				
			||||
				AXmlElement topHalf = new AXmlElement(); | 
				
			||||
				topHalf.HasStartOrEmptyTag = elem.HasStartOrEmptyTag; | 
				
			||||
				topHalf.HasEndTag = elem.HasEndTag; | 
				
			||||
				topHalf.AddChildren(elem.Children.Take(1 + lastAccepted));    // Start tag + nested
 | 
				
			||||
				topHalf.StartOffset = topHalf.FirstChild.StartOffset; | 
				
			||||
				topHalf.EndOffset = topHalf.LastChild.EndOffset; | 
				
			||||
				TagReader.OnSyntaxError(topHalf, topHalf.LastChild.EndOffset, topHalf.LastChild.EndOffset, | 
				
			||||
						                 "Expected '</{0}>'", topHalf.StartTag.Name); | 
				
			||||
				 | 
				
			||||
				AXmlParser.Log("Constructed {0}", topHalf); | 
				
			||||
				trackedSegments.AddParsedObject(topHalf, null); | 
				
			||||
				yield return topHalf; | 
				
			||||
				for(int i = lastAccepted + 1; i < elem.Children.Count; i++) { | 
				
			||||
					yield return elem.Children[i]; | 
				
			||||
				} | 
				
			||||
			} else { | 
				
			||||
				yield return elem; | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		int GetIndentLevel(AXmlObject obj) | 
				
			||||
		{ | 
				
			||||
			int offset = obj.StartOffset - 1; | 
				
			||||
			int level = 0; | 
				
			||||
			while(true) { | 
				
			||||
				if (offset < 0) break; | 
				
			||||
				char c = input[offset]; | 
				
			||||
				if (c == ' ') { | 
				
			||||
					level++; | 
				
			||||
				} else if (c == '\t') { | 
				
			||||
					level += 4; | 
				
			||||
				} else if (c == '\r' || c == '\n') { | 
				
			||||
					break; | 
				
			||||
				} else { | 
				
			||||
					return -1; | 
				
			||||
				} | 
				
			||||
				offset--; | 
				
			||||
			} | 
				
			||||
			return level; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Stack of still unmatched start tags.
 | 
				
			||||
		/// It includes the cost and backtack information.
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		class Configuration | 
				
			||||
		{ | 
				
			||||
			/// <summary> Unmatched start tags </summary>
 | 
				
			||||
			public ImmutableStack<AXmlTag> StartTags { get; set; } | 
				
			||||
			/// <summary> Properly nested tags </summary>
 | 
				
			||||
			public ImmutableStack<AXmlObject> Document { get; set; } | 
				
			||||
			/// <summary> Number of needed modificaitons to the document </summary>
 | 
				
			||||
			public int Cost { get; set; } | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Dictionary which stores the cheapest configuration
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		class Configurations: Dictionary<ImmutableStack<AXmlTag>, Configuration> | 
				
			||||
		{ | 
				
			||||
			public Configurations() | 
				
			||||
			{ | 
				
			||||
			} | 
				
			||||
			 | 
				
			||||
			public Configurations(IEnumerable<Configuration> configs) | 
				
			||||
			{ | 
				
			||||
				foreach(Configuration config in configs) { | 
				
			||||
					this.Add(config); | 
				
			||||
				} | 
				
			||||
			} | 
				
			||||
			 | 
				
			||||
			/// <summary> Overwrite only if cheaper </summary>
 | 
				
			||||
			public void Add(Configuration newConfig) | 
				
			||||
			{ | 
				
			||||
				Configuration oldConfig; | 
				
			||||
				if (this.TryGetValue(newConfig.StartTags, out oldConfig)) { | 
				
			||||
					if (newConfig.Cost < oldConfig.Cost) { | 
				
			||||
						this[newConfig.StartTags] = newConfig; | 
				
			||||
					} | 
				
			||||
				} else { | 
				
			||||
					base.Add(newConfig.StartTags, newConfig); | 
				
			||||
				} | 
				
			||||
			} | 
				
			||||
			 | 
				
			||||
			public override string ToString() | 
				
			||||
			{ | 
				
			||||
				StringBuilder sb = new StringBuilder(); | 
				
			||||
				foreach(var kvp in this) { | 
				
			||||
					sb.Append("\n - '"); | 
				
			||||
					foreach(AXmlTag startTag in kvp.Value.StartTags.Reverse()) { | 
				
			||||
						sb.Append('<'); | 
				
			||||
						sb.Append(startTag.Name); | 
				
			||||
						sb.Append('>'); | 
				
			||||
					} | 
				
			||||
					sb.AppendFormat("' = {0}", kvp.Value.Cost); | 
				
			||||
				} | 
				
			||||
				return sb.ToString(); | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		// Tags used to guide the element creation
 | 
				
			||||
		readonly AXmlTag StartTagPlaceholder = new AXmlTag(); | 
				
			||||
		readonly AXmlTag EndTagPlaceholder = new AXmlTag(); | 
				
			||||
		 | 
				
			||||
		/// <summary>
 | 
				
			||||
		/// Add start or end tag placeholders so that the documment is properly nested
 | 
				
			||||
		/// </summary>
 | 
				
			||||
		List<AXmlObject> MatchTags(IEnumerable<AXmlObject> objs) | 
				
			||||
		{ | 
				
			||||
			Configurations configurations = new Configurations(); | 
				
			||||
			configurations.Add(new Configuration { | 
				
			||||
				StartTags = ImmutableStack<AXmlTag>.Empty, | 
				
			||||
				Document = ImmutableStack<AXmlObject>.Empty, | 
				
			||||
				Cost = 0, | 
				
			||||
			}); | 
				
			||||
			foreach(AXmlObject obj in objs) { | 
				
			||||
				configurations = ProcessObject(configurations, obj); | 
				
			||||
			} | 
				
			||||
			// Close any remaining start tags
 | 
				
			||||
			foreach(Configuration conifg in configurations.Values) { | 
				
			||||
				while(!conifg.StartTags.IsEmpty) { | 
				
			||||
					conifg.StartTags = conifg.StartTags.Pop(); | 
				
			||||
					conifg.Document = conifg.Document.Push(EndTagPlaceholder); | 
				
			||||
					conifg.Cost += 1; | 
				
			||||
				} | 
				
			||||
			} | 
				
			||||
			// AXmlParser.Log("Configurations after closing all remaining tags:" + configurations.ToString());
 | 
				
			||||
			Configuration bestConfig = configurations.Values.OrderBy(v => v.Cost).First(); | 
				
			||||
			AXmlParser.Log("Best configuration has cost {0}", bestConfig.Cost); | 
				
			||||
			 | 
				
			||||
			return bestConfig.Document.Reverse().ToList(); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		/// <summary> Get posible configurations after considering given object </summary>
 | 
				
			||||
		Configurations ProcessObject(Configurations oldConfigs, AXmlObject obj) | 
				
			||||
		{ | 
				
			||||
			AXmlParser.Log("Processing {0}", obj); | 
				
			||||
			 | 
				
			||||
			AXmlTag objAsTag = obj as AXmlTag; | 
				
			||||
			AXmlElement objAsElement = obj as AXmlElement; | 
				
			||||
			AXmlParser.DebugAssert(objAsTag != null || objAsElement != null || obj is AXmlText, obj.GetType().Name + " not expected"); | 
				
			||||
			if (objAsElement != null) | 
				
			||||
				AXmlParser.Assert(objAsElement.IsProperlyNested, "Element not properly nested"); | 
				
			||||
			 | 
				
			||||
			Configurations newConfigs = new Configurations(); | 
				
			||||
			 | 
				
			||||
			foreach(var kvp in oldConfigs) { | 
				
			||||
				Configuration oldConfig = kvp.Value; | 
				
			||||
				var oldStartTags = oldConfig.StartTags; | 
				
			||||
				var oldDocument = oldConfig.Document; | 
				
			||||
				int oldCost = oldConfig.Cost; | 
				
			||||
				 | 
				
			||||
				if (objAsTag != null && objAsTag.IsStartTag) { | 
				
			||||
					newConfigs.Add(new Configuration {                    // Push start-tag (cost 0)
 | 
				
			||||
						StartTags = oldStartTags.Push(objAsTag), | 
				
			||||
						Document = oldDocument.Push(objAsTag), | 
				
			||||
						Cost = oldCost, | 
				
			||||
					}); | 
				
			||||
				} else if (objAsTag != null && objAsTag.IsEndTag) { | 
				
			||||
					newConfigs.Add(new Configuration {                    // Ignore (cost 1)
 | 
				
			||||
						StartTags = oldStartTags, | 
				
			||||
						Document = oldDocument.Push(StartTagPlaceholder).Push(objAsTag), | 
				
			||||
						Cost = oldCost + 1, | 
				
			||||
	               }); | 
				
			||||
					if (!oldStartTags.IsEmpty && oldStartTags.Peek().Name != objAsTag.Name) { | 
				
			||||
						newConfigs.Add(new Configuration {                // Pop 1 item (cost 1) - not mathcing
 | 
				
			||||
							StartTags = oldStartTags.Pop(), | 
				
			||||
							Document = oldDocument.Push(objAsTag), | 
				
			||||
							Cost = oldCost + 1, | 
				
			||||
		               }); | 
				
			||||
					} | 
				
			||||
					int popedCount = 0; | 
				
			||||
					var startTags = oldStartTags; | 
				
			||||
					var doc = oldDocument; | 
				
			||||
					foreach(AXmlTag poped in oldStartTags) { | 
				
			||||
						popedCount++; | 
				
			||||
						if (poped.Name == objAsTag.Name) { | 
				
			||||
							newConfigs.Add(new Configuration {             // Pop 'x' items (cost x-1) - last one is matching
 | 
				
			||||
								StartTags = startTags.Pop(), | 
				
			||||
								Document = doc.Push(objAsTag), | 
				
			||||
								Cost = oldCost + popedCount - 1, | 
				
			||||
							}); | 
				
			||||
						} | 
				
			||||
						startTags = startTags.Pop(); | 
				
			||||
						doc = doc.Push(EndTagPlaceholder); | 
				
			||||
					} | 
				
			||||
				} else { | 
				
			||||
					// Empty tag  or  other tag type  or  text  or  properly nested element
 | 
				
			||||
					newConfigs.Add(new Configuration {                    // Ignore (cost 0)
 | 
				
			||||
						StartTags = oldStartTags, | 
				
			||||
						Document = oldDocument.Push(obj), | 
				
			||||
						Cost = oldCost, | 
				
			||||
	               }); | 
				
			||||
				} | 
				
			||||
			} | 
				
			||||
			 | 
				
			||||
			// Log("New configurations:" + newConfigs.ToString());
 | 
				
			||||
			 | 
				
			||||
			Configurations bestNewConfigurations = new Configurations( | 
				
			||||
				newConfigs.Values.OrderBy(v => v.Cost).Take(maxConfigurationCount) | 
				
			||||
			); | 
				
			||||
			 | 
				
			||||
			// AXmlParser.Log("Best new configurations:" + bestNewConfigurations.ToString());
 | 
				
			||||
			 | 
				
			||||
			return bestNewConfigurations; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		#region Helper methods
 | 
				
			||||
		/* | 
				
			||||
		string PrintObjects(IEnumerable<AXmlObject> objs) | 
				
			||||
		{ | 
				
			||||
			StringBuilder sb = new StringBuilder(); | 
				
			||||
			foreach(AXmlObject obj in objs) { | 
				
			||||
				if (obj is AXmlTag) { | 
				
			||||
					if (obj == StartTagPlaceholder) { | 
				
			||||
						sb.Append("#StartTag#"); | 
				
			||||
					} else if (obj == EndTagPlaceholder) { | 
				
			||||
						sb.Append("#EndTag#"); | 
				
			||||
					} else { | 
				
			||||
						sb.Append(((AXmlTag)obj).OpeningBracket); | 
				
			||||
						sb.Append(((AXmlTag)obj).Name); | 
				
			||||
						sb.Append(((AXmlTag)obj).ClosingBracket); | 
				
			||||
					} | 
				
			||||
				} else if (obj is AXmlElement) { | 
				
			||||
					sb.Append('['); | 
				
			||||
					sb.Append(PrintObjects(((AXmlElement)obj).Children)); | 
				
			||||
					sb.Append(']'); | 
				
			||||
				} else if (obj is AXmlText) { | 
				
			||||
					sb.Append('~'); | 
				
			||||
				} else { | 
				
			||||
					throw new InternalException("Should not be here: " + obj); | 
				
			||||
				} | 
				
			||||
			} | 
				
			||||
			return sb.ToString(); | 
				
			||||
		} | 
				
			||||
		*/ | 
				
			||||
		#endregion
 | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
					Loading…
					
					
				
		Reference in new issue