Browse Source
git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4912 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61shortcuts
4 changed files with 260 additions and 6 deletions
@ -0,0 +1,216 @@
@@ -0,0 +1,216 @@
|
||||
// <file>
|
||||
// <copyright see="prj:///doc/copyright.txt"/>
|
||||
// <license see="prj:///doc/license.txt"/>
|
||||
// <owner name="Daniel Grunwald"/>
|
||||
// <version>$Revision$</version>
|
||||
// </file>
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using System.Text; |
||||
using System.Xml; |
||||
|
||||
using ICSharpCode.AvalonEdit.Document; |
||||
|
||||
namespace ICSharpCode.AvalonEdit.Folding |
||||
{ |
||||
/// <summary>
|
||||
/// Holds information about the start of a fold in an xml string.
|
||||
/// </summary>
|
||||
sealed class XmlFoldStart : NewFolding |
||||
{ |
||||
internal int StartLine; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Determines folds for an xml string in the editor.
|
||||
/// </summary>
|
||||
public class XmlFoldingStrategy : AbstractFoldingStrategy |
||||
{ |
||||
/// <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.
|
||||
/// </summary>
|
||||
public override 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: |
||||
CreateCommentFold(document, foldMarkers, reader); |
||||
break; |
||||
} |
||||
} |
||||
firstErrorOffset = -1; |
||||
} catch (XmlException ex) { |
||||
// ignore errors
|
||||
firstErrorOffset = document.GetOffset(ex.LineNumber, ex.LinePosition); |
||||
} |
||||
foldMarkers.Sort((a,b) => a.StartOffset.CompareTo(b.StartOffset)); |
||||
return foldMarkers; |
||||
} |
||||
|
||||
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>
|
||||
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 + 3; |
||||
|
||||
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>
|
||||
/// Create an element fold if the start and end tag are on
|
||||
/// different lines.
|
||||
/// </summary>
|
||||
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>
|
||||
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("&", "&"); |
||||
encodedValue.Replace("<", "<"); |
||||
encodedValue.Replace(">", ">"); |
||||
|
||||
if (quoteChar == '"') { |
||||
encodedValue.Replace("\"", """); |
||||
} else { |
||||
encodedValue.Replace("'", "'"); |
||||
} |
||||
|
||||
return encodedValue.ToString(); |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue