Skip to content

Commit

Permalink
xmlcomment with regions
Browse files Browse the repository at this point in the history
  • Loading branch information
robertmuehsig committed Apr 5, 2018
1 parent e8b2a0d commit 6e4ddde
Show file tree
Hide file tree
Showing 27 changed files with 2,773 additions and 0 deletions.
6 changes: 6 additions & 0 deletions 2018/XmlIntelliSenseXmlComment/XmlIntelliSense.App/App.config
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
</configuration>
9 changes: 9 additions & 0 deletions 2018/XmlIntelliSenseXmlComment/XmlIntelliSense.App/App.xaml
@@ -0,0 +1,9 @@
<Application x:Class="XmlIntelliSense.App.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:XmlIntelliSense.App"
StartupUri="MainWindow.xaml">
<Application.Resources>

</Application.Resources>
</Application>
18 changes: 18 additions & 0 deletions 2018/XmlIntelliSenseXmlComment/XmlIntelliSense.App/App.xaml.cs
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;

namespace XmlIntelliSense.App
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{

}
}
@@ -0,0 +1,293 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Folding;

namespace XmlIntelliSense.App
{
/// <summary>
/// Determines folds for an xml string in the editor.
/// </summary>
public class CustomXmlFoldingStrategy
{
/// <summary>
/// Flag indicating whether attributes should be displayed on folded
/// elements.
/// </summary>
public bool ShowAttributesWhenFolded { get; set; }

/// <summary>
/// Create <see cref="NewFolding"/>s for the specified document and updates the folding manager with them.
/// </summary>
public void UpdateFoldings(FoldingManager manager, TextDocument document)
{
int firstErrorOffset;
IEnumerable<NewFolding> foldings = CreateNewFoldings(document, out firstErrorOffset);
manager.UpdateFoldings(foldings, firstErrorOffset);
}

/// <summary>
/// Create <see cref="NewFolding"/>s for the specified document.
/// </summary>
public IEnumerable<NewFolding> CreateNewFoldings(TextDocument document, out int firstErrorOffset)
{
try
{
XmlTextReader reader = new XmlTextReader(document.CreateReader());
reader.XmlResolver = null; // don't resolve DTDs
return CreateNewFoldings(document, reader, out firstErrorOffset);
}
catch (XmlException)
{
firstErrorOffset = 0;
return Enumerable.Empty<NewFolding>();
}
}

/// <summary>
/// Create <see cref="NewFolding"/>s for the specified document.
/// </summary>
public IEnumerable<NewFolding> CreateNewFoldings(TextDocument document, XmlReader reader, out int firstErrorOffset)
{
Stack<XmlFoldStart> stack = new Stack<XmlFoldStart>();
List<NewFolding> foldMarkers = new List<NewFolding>();
try
{
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
if (!reader.IsEmptyElement)
{
XmlFoldStart newFoldStart = CreateElementFoldStart(document, reader);
stack.Push(newFoldStart);
}
break;

case XmlNodeType.EndElement:
XmlFoldStart foldStart = stack.Pop();
CreateElementFold(document, foldMarkers, reader, foldStart);
break;

case XmlNodeType.Comment:
if (reader.Value.TrimStart().StartsWith("#region"))
{
XmlFoldStart newFoldStartComment = CreateCommentRegionFoldStart(document, reader);
stack.Push(newFoldStartComment);
}
else if (reader.Value.Trim().StartsWith("#endregion"))
{
XmlFoldStart foldStartComent = stack.Pop();
CreateCommentRegionFold(document, foldMarkers, reader, foldStartComent);
}
else
{
CreateCommentFold(document, foldMarkers, reader);
}

break;
}
}
firstErrorOffset = -1;
}
catch (XmlException ex)
{
// ignore errors at invalid positions (prevent ArgumentOutOfRangeException)
if (ex.LineNumber >= 1 && ex.LineNumber <= document.LineCount)
firstErrorOffset = document.GetOffset(ex.LineNumber, ex.LinePosition);
else
firstErrorOffset = 0;
}
foldMarkers.Sort((a, b) => a.StartOffset.CompareTo(b.StartOffset));
return foldMarkers;
}

static int GetOffset(TextDocument document, XmlReader reader)
{
IXmlLineInfo info = reader as IXmlLineInfo;
if (info != null && info.HasLineInfo())
{
return document.GetOffset(info.LineNumber, info.LinePosition);
}
else
{
throw new ArgumentException("XmlReader does not have positioning information.");
}
}

/// <summary>
/// Creates a comment fold if the comment spans more than one line.
/// </summary>
/// <remarks>The text displayed when the comment is folded is the first
/// line of the comment.</remarks>
static void CreateCommentFold(TextDocument document, List<NewFolding> foldMarkers, XmlReader reader)
{
string comment = reader.Value;
if (comment != null)
{
int firstNewLine = comment.IndexOf('\n');
if (firstNewLine >= 0)
{

// Take off 4 chars to get the actual comment start (takes
// into account the <!-- chars.

int startOffset = GetOffset(document, reader) - 4;
int endOffset = startOffset + comment.Length + 7;

string foldText = String.Concat("<!--", comment.Substring(0, firstNewLine).TrimEnd('\r'), "-->");
foldMarkers.Add(new NewFolding(startOffset, endOffset) { Name = foldText });
}
}
}

/// <summary>
/// Creates an XmlFoldStart for the start tag of an element.
/// </summary>
XmlFoldStart CreateElementFoldStart(TextDocument document, XmlReader reader)
{
// Take off 1 from the offset returned
// from the xml since it points to the start
// of the element name and not the beginning
// tag.
//XmlFoldStart newFoldStart = new XmlFoldStart(reader.Prefix, reader.LocalName, reader.LineNumber - 1, reader.LinePosition - 2);
XmlFoldStart newFoldStart = new XmlFoldStart();

IXmlLineInfo lineInfo = (IXmlLineInfo)reader;
newFoldStart.StartLine = lineInfo.LineNumber;
newFoldStart.StartOffset = document.GetOffset(newFoldStart.StartLine, lineInfo.LinePosition - 1);

if (this.ShowAttributesWhenFolded && reader.HasAttributes)
{
newFoldStart.Name = String.Concat("<", reader.Name, " ", GetAttributeFoldText(reader), ">");
}
else
{
newFoldStart.Name = String.Concat("<", reader.Name, ">");
}

return newFoldStart;
}

/// <summary>
/// Creates an XmlFoldStart for the start tag of an element.
/// </summary>
XmlFoldStart CreateCommentRegionFoldStart(TextDocument document, XmlReader reader)
{
// Take off 1 from the offset returned
// from the xml since it points to the start
// of the element name and not the beginning
// tag.
//XmlFoldStart newFoldStart = new XmlFoldStart(reader.Prefix, reader.LocalName, reader.LineNumber - 1, reader.LinePosition - 2);
XmlFoldStart newFoldStart = new XmlFoldStart();

IXmlLineInfo lineInfo = (IXmlLineInfo)reader;
newFoldStart.StartLine = lineInfo.LineNumber;
newFoldStart.StartOffset = document.GetOffset(newFoldStart.StartLine, lineInfo.LinePosition - 4);

newFoldStart.Name = reader.Value.Replace("#region", "");

//if (this.ShowAttributesWhenFolded && reader.HasAttributes)
//{
// newFoldStart.Name = String.Concat("<", reader.Name, " ", GetAttributeFoldText(reader), ">");
//}
//else
//{
// newFoldStart.Name = String.Concat("<", reader.Name, ">");
//}

return newFoldStart;
}

static void CreateCommentRegionFold(TextDocument document, List<NewFolding> foldMarkers, XmlReader reader, XmlFoldStart foldStart)
{
IXmlLineInfo lineInfo = (IXmlLineInfo)reader;
int endLine = lineInfo.LineNumber;
if (endLine > foldStart.StartLine)
{
int endCol = lineInfo.LinePosition + 15;
foldStart.EndOffset = document.GetOffset(endLine, endCol);
foldMarkers.Add(foldStart);
}
}

/// <summary>
/// Create an element fold if the start and end tag are on
/// different lines.
/// </summary>
static void CreateElementFold(TextDocument document, List<NewFolding> foldMarkers, XmlReader reader, XmlFoldStart foldStart)
{
IXmlLineInfo lineInfo = (IXmlLineInfo)reader;
int endLine = lineInfo.LineNumber;
if (endLine > foldStart.StartLine)
{
int endCol = lineInfo.LinePosition + reader.Name.Length + 1;
foldStart.EndOffset = document.GetOffset(endLine, endCol);
foldMarkers.Add(foldStart);
}
}

/// <summary>
/// Gets the element's attributes as a string on one line that will
/// be displayed when the element is folded.
/// </summary>
/// <remarks>
/// Currently this puts all attributes from an element on the same
/// line of the start tag. It does not cater for elements where attributes
/// are not on the same line as the start tag.
/// </remarks>
static string GetAttributeFoldText(XmlReader reader)
{
StringBuilder text = new StringBuilder();

for (int i = 0; i < reader.AttributeCount; ++i)
{
reader.MoveToAttribute(i);

text.Append(reader.Name);
text.Append("=");
text.Append(reader.QuoteChar.ToString());
text.Append(XmlEncodeAttributeValue(reader.Value, reader.QuoteChar));
text.Append(reader.QuoteChar.ToString());

// Append a space if this is not the
// last attribute.
if (i < reader.AttributeCount - 1)
{
text.Append(" ");
}
}

return text.ToString();
}

/// <summary>
/// Xml encode the attribute string since the string returned from
/// the XmlTextReader is the plain unencoded string and .NET
/// does not provide us with an xml encode method.
/// </summary>
static string XmlEncodeAttributeValue(string attributeValue, char quoteChar)
{
StringBuilder encodedValue = new StringBuilder(attributeValue);

encodedValue.Replace("&", "&amp;");
encodedValue.Replace("<", "&lt;");
encodedValue.Replace(">", "&gt;");

if (quoteChar == '"')
{
encodedValue.Replace("\"", "&quot;");
}
else
{
encodedValue.Replace("'", "&apos;");
}

return encodedValue.ToString();
}
}
}
27 changes: 27 additions & 0 deletions 2018/XmlIntelliSenseXmlComment/XmlIntelliSense.App/MainWindow.xaml
@@ -0,0 +1,27 @@
<Window x:Class="XmlIntelliSense.App.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:XmlIntelliSense.App"
mc:Ignorable="d"
Title="MainWindow" Height="850" Width="825">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50*" />
<RowDefinition Height="40*"/>
<RowDefinition Height="10*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<avalonEdit:TextEditor
xmlns:avalonEdit="http://icsharpcode.net/sharpdevelop/avalonedit"
Name="textEditor"
FontFamily="Consolas"
SyntaxHighlighting="C#"
FontSize="10pt" Grid.Row="0"/>
<TextBox AcceptsReturn="True" Grid.Row="1" Name="XSD"></TextBox>
<TextBox Grid.Row="2" Name="CurrentPath" />
</Grid>
</Window>

0 comments on commit 6e4ddde

Please sign in to comment.