mirror of https://github.com/icsharpcode/ILSpy.git
5 changed files with 119 additions and 1197 deletions
@ -1,448 +0,0 @@ |
|||||||
// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
|
||||||
// software and associated documentation files (the "Software"), to deal in the Software
|
|
||||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
|
||||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
|
||||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all copies or
|
|
||||||
// substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
||||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
||||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
|
||||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
||||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
// DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
using System; |
|
||||||
using System.Collections.Generic; |
|
||||||
using ICSharpCode.AvalonEdit.Document; |
|
||||||
|
|
||||||
namespace ICSharpCode.ILSpy.TextView |
|
||||||
{ |
|
||||||
/// <summary>
|
|
||||||
/// Read-only implementation of <see cref="IDocument"/>.
|
|
||||||
/// </summary>
|
|
||||||
[Serializable] |
|
||||||
public sealed class ReadOnlyDocument : IDocument |
|
||||||
{ |
|
||||||
readonly ITextSource textSource; |
|
||||||
readonly string fileName; |
|
||||||
int[] lines; |
|
||||||
|
|
||||||
static readonly char[] newline = { '\r', '\n' }; |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new ReadOnlyDocument from the given text source.
|
|
||||||
/// </summary>
|
|
||||||
public ReadOnlyDocument(ITextSource textSource) |
|
||||||
{ |
|
||||||
if (textSource == null) |
|
||||||
throw new ArgumentNullException("textSource"); |
|
||||||
// ensure that underlying buffer is immutable
|
|
||||||
this.textSource = textSource.CreateSnapshot(); |
|
||||||
List<int> lines = new List<int>(); |
|
||||||
lines.Add(0); |
|
||||||
int offset = 0; |
|
||||||
int textLength = textSource.TextLength; |
|
||||||
while ((offset = textSource.IndexOfAny(newline, offset, textLength - offset)) >= 0) { |
|
||||||
offset++; |
|
||||||
if (textSource.GetCharAt(offset - 1) == '\r' && offset < textLength && textSource.GetCharAt(offset) == '\n') { |
|
||||||
offset++; |
|
||||||
} |
|
||||||
lines.Add(offset); |
|
||||||
} |
|
||||||
this.lines = lines.ToArray(); |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new ReadOnlyDocument from the given string.
|
|
||||||
/// </summary>
|
|
||||||
public ReadOnlyDocument(string text) |
|
||||||
: this(new StringTextSource(text)) |
|
||||||
{ |
|
||||||
} |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new ReadOnlyDocument from the given text source;
|
|
||||||
/// and sets IDocument.FileName to the specified file name.
|
|
||||||
/// </summary>
|
|
||||||
public ReadOnlyDocument(ITextSource textSource, string fileName) |
|
||||||
: this(textSource) |
|
||||||
{ |
|
||||||
this.fileName = fileName; |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public IDocumentLine GetLineByNumber(int lineNumber) |
|
||||||
{ |
|
||||||
if (lineNumber < 1 || lineNumber > lines.Length) |
|
||||||
throw new ArgumentOutOfRangeException("lineNumber", lineNumber, "Value must be between 1 and " + lines.Length); |
|
||||||
return new ReadOnlyDocumentLine(this, lineNumber); |
|
||||||
} |
|
||||||
|
|
||||||
sealed class ReadOnlyDocumentLine : IDocumentLine |
|
||||||
{ |
|
||||||
readonly ReadOnlyDocument doc; |
|
||||||
readonly int lineNumber; |
|
||||||
readonly int offset, endOffset; |
|
||||||
|
|
||||||
public ReadOnlyDocumentLine(ReadOnlyDocument doc, int lineNumber) |
|
||||||
{ |
|
||||||
this.doc = doc; |
|
||||||
this.lineNumber = lineNumber; |
|
||||||
this.offset = doc.GetStartOffset(lineNumber); |
|
||||||
this.endOffset = doc.GetEndOffset(lineNumber); |
|
||||||
} |
|
||||||
|
|
||||||
public override int GetHashCode() |
|
||||||
{ |
|
||||||
return doc.GetHashCode() ^ lineNumber; |
|
||||||
} |
|
||||||
|
|
||||||
public override bool Equals(object obj) |
|
||||||
{ |
|
||||||
ReadOnlyDocumentLine other = obj as ReadOnlyDocumentLine; |
|
||||||
return other != null && doc == other.doc && lineNumber == other.lineNumber; |
|
||||||
} |
|
||||||
|
|
||||||
public int Offset { |
|
||||||
get { return offset; } |
|
||||||
} |
|
||||||
|
|
||||||
public int Length { |
|
||||||
get { return endOffset - offset; } |
|
||||||
} |
|
||||||
|
|
||||||
public int EndOffset { |
|
||||||
get { return endOffset; } |
|
||||||
} |
|
||||||
|
|
||||||
public int TotalLength { |
|
||||||
get { |
|
||||||
return doc.GetTotalEndOffset(lineNumber) - offset; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public int DelimiterLength { |
|
||||||
get { |
|
||||||
return doc.GetTotalEndOffset(lineNumber) - endOffset; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public int LineNumber { |
|
||||||
get { return lineNumber; } |
|
||||||
} |
|
||||||
|
|
||||||
public IDocumentLine PreviousLine { |
|
||||||
get { |
|
||||||
if (lineNumber == 1) |
|
||||||
return null; |
|
||||||
else |
|
||||||
return new ReadOnlyDocumentLine(doc, lineNumber - 1); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public IDocumentLine NextLine { |
|
||||||
get { |
|
||||||
if (lineNumber == doc.LineCount) |
|
||||||
return null; |
|
||||||
else |
|
||||||
return new ReadOnlyDocumentLine(doc, lineNumber + 1); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public bool IsDeleted { |
|
||||||
get { return false; } |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
int GetStartOffset(int lineNumber) |
|
||||||
{ |
|
||||||
return lines[lineNumber - 1]; |
|
||||||
} |
|
||||||
|
|
||||||
int GetTotalEndOffset(int lineNumber) |
|
||||||
{ |
|
||||||
return lineNumber < lines.Length ? lines[lineNumber] : textSource.TextLength; |
|
||||||
} |
|
||||||
|
|
||||||
int GetEndOffset(int lineNumber) |
|
||||||
{ |
|
||||||
if (lineNumber == lines.Length) |
|
||||||
return textSource.TextLength; |
|
||||||
int off = lines[lineNumber] - 1; |
|
||||||
if (off > 0 && textSource.GetCharAt(off - 1) == '\r' && textSource.GetCharAt(off) == '\n') |
|
||||||
off--; |
|
||||||
return off; |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public IDocumentLine GetLineByOffset(int offset) |
|
||||||
{ |
|
||||||
return GetLineByNumber(GetLineNumberForOffset(offset)); |
|
||||||
} |
|
||||||
|
|
||||||
int GetLineNumberForOffset(int offset) |
|
||||||
{ |
|
||||||
int r = Array.BinarySearch(lines, offset); |
|
||||||
return r < 0 ? ~r : r + 1; |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int GetOffset(int line, int column) |
|
||||||
{ |
|
||||||
if (line < 1 || line > lines.Length) |
|
||||||
throw new ArgumentOutOfRangeException("line", line, "Value must be between 1 and " + lines.Length); |
|
||||||
int lineStart = GetStartOffset(line); |
|
||||||
if (column <= 1) |
|
||||||
return lineStart; |
|
||||||
int lineEnd = GetEndOffset(line); |
|
||||||
if (column - 1 >= lineEnd - lineStart) |
|
||||||
return lineEnd; |
|
||||||
return lineStart + column - 1; |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int GetOffset(TextLocation location) |
|
||||||
{ |
|
||||||
return GetOffset(location.Line, location.Column); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public TextLocation GetLocation(int offset) |
|
||||||
{ |
|
||||||
if (offset < 0 || offset > textSource.TextLength) |
|
||||||
throw new ArgumentOutOfRangeException("offset", offset, "Value must be between 0 and " + textSource.TextLength); |
|
||||||
int line = GetLineNumberForOffset(offset); |
|
||||||
return new TextLocation(line, offset - GetStartOffset(line) + 1); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public string Text { |
|
||||||
get { return textSource.Text; } |
|
||||||
set { |
|
||||||
throw new NotSupportedException(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int LineCount { |
|
||||||
get { return lines.Length; } |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public ITextSourceVersion Version { |
|
||||||
get { return textSource.Version; } |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int TextLength { |
|
||||||
get { return textSource.TextLength; } |
|
||||||
} |
|
||||||
|
|
||||||
event EventHandler<TextChangeEventArgs> IDocument.TextChanging { add { } remove { } } |
|
||||||
|
|
||||||
event EventHandler<TextChangeEventArgs> IDocument.TextChanged { add { } remove { } } |
|
||||||
|
|
||||||
event EventHandler IDocument.ChangeCompleted { add { } remove { } } |
|
||||||
|
|
||||||
void IDocument.Insert(int offset, string text) |
|
||||||
{ |
|
||||||
throw new NotSupportedException(); |
|
||||||
} |
|
||||||
|
|
||||||
void IDocument.Insert(int offset, string text, AnchorMovementType defaultAnchorMovementType) |
|
||||||
{ |
|
||||||
throw new NotSupportedException(); |
|
||||||
} |
|
||||||
|
|
||||||
void IDocument.Remove(int offset, int length) |
|
||||||
{ |
|
||||||
throw new NotSupportedException(); |
|
||||||
} |
|
||||||
|
|
||||||
void IDocument.Replace(int offset, int length, string newText) |
|
||||||
{ |
|
||||||
throw new NotSupportedException(); |
|
||||||
} |
|
||||||
|
|
||||||
void IDocument.Insert(int offset, ITextSource text) |
|
||||||
{ |
|
||||||
throw new NotSupportedException(); |
|
||||||
} |
|
||||||
|
|
||||||
void IDocument.Insert(int offset, ITextSource text, AnchorMovementType defaultAnchorMovementType) |
|
||||||
{ |
|
||||||
throw new NotSupportedException(); |
|
||||||
} |
|
||||||
|
|
||||||
void IDocument.Replace(int offset, int length, ITextSource newText) |
|
||||||
{ |
|
||||||
throw new NotSupportedException(); |
|
||||||
} |
|
||||||
|
|
||||||
void IDocument.StartUndoableAction() |
|
||||||
{ |
|
||||||
} |
|
||||||
|
|
||||||
void IDocument.EndUndoableAction() |
|
||||||
{ |
|
||||||
} |
|
||||||
|
|
||||||
IDisposable IDocument.OpenUndoGroup() |
|
||||||
{ |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public ITextAnchor CreateAnchor(int offset) |
|
||||||
{ |
|
||||||
return new ReadOnlyDocumentTextAnchor(GetLocation(offset), offset); |
|
||||||
} |
|
||||||
|
|
||||||
sealed class ReadOnlyDocumentTextAnchor : ITextAnchor |
|
||||||
{ |
|
||||||
readonly TextLocation location; |
|
||||||
readonly int offset; |
|
||||||
|
|
||||||
public ReadOnlyDocumentTextAnchor(TextLocation location, int offset) |
|
||||||
{ |
|
||||||
this.location = location; |
|
||||||
this.offset = offset; |
|
||||||
} |
|
||||||
|
|
||||||
public event EventHandler Deleted { add { } remove { } } |
|
||||||
|
|
||||||
public TextLocation Location { |
|
||||||
get { return location; } |
|
||||||
} |
|
||||||
|
|
||||||
public int Offset { |
|
||||||
get { return offset; } |
|
||||||
} |
|
||||||
|
|
||||||
public AnchorMovementType MovementType { get; set; } |
|
||||||
|
|
||||||
public bool SurviveDeletion { get; set; } |
|
||||||
|
|
||||||
public bool IsDeleted { |
|
||||||
get { return false; } |
|
||||||
} |
|
||||||
|
|
||||||
public int Line { |
|
||||||
get { return location.Line; } |
|
||||||
} |
|
||||||
|
|
||||||
public int Column { |
|
||||||
get { return location.Column; } |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public ITextSource CreateSnapshot() |
|
||||||
{ |
|
||||||
return textSource; // textBuffer is immutable
|
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public ITextSource CreateSnapshot(int offset, int length) |
|
||||||
{ |
|
||||||
return textSource.CreateSnapshot(offset, length); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public IDocument CreateDocumentSnapshot() |
|
||||||
{ |
|
||||||
return this; // ReadOnlyDocument is immutable
|
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public System.IO.TextReader CreateReader() |
|
||||||
{ |
|
||||||
return textSource.CreateReader(); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public System.IO.TextReader CreateReader(int offset, int length) |
|
||||||
{ |
|
||||||
return textSource.CreateReader(offset, length); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void WriteTextTo(System.IO.TextWriter writer) |
|
||||||
{ |
|
||||||
textSource.WriteTextTo(writer); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void WriteTextTo(System.IO.TextWriter writer, int offset, int length) |
|
||||||
{ |
|
||||||
textSource.WriteTextTo(writer, offset, length); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public char GetCharAt(int offset) |
|
||||||
{ |
|
||||||
return textSource.GetCharAt(offset); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public string GetText(int offset, int length) |
|
||||||
{ |
|
||||||
return textSource.GetText(offset, length); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public string GetText(ISegment segment) |
|
||||||
{ |
|
||||||
return textSource.GetText(segment); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int IndexOf(char c, int startIndex, int count) |
|
||||||
{ |
|
||||||
return textSource.IndexOf(c, startIndex, count); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int IndexOfAny(char[] anyOf, int startIndex, int count) |
|
||||||
{ |
|
||||||
return textSource.IndexOfAny(anyOf, startIndex, count); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int IndexOf(string searchText, int startIndex, int count, StringComparison comparisonType) |
|
||||||
{ |
|
||||||
return textSource.IndexOf(searchText, startIndex, count, comparisonType); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int LastIndexOf(char c, int startIndex, int count) |
|
||||||
{ |
|
||||||
return textSource.LastIndexOf(c, startIndex, count); |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public int LastIndexOf(string searchText, int startIndex, int count, StringComparison comparisonType) |
|
||||||
{ |
|
||||||
return textSource.LastIndexOf(searchText, startIndex, count, comparisonType); |
|
||||||
} |
|
||||||
|
|
||||||
object IServiceProvider.GetService(Type serviceType) |
|
||||||
{ |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
/// <remarks>Will never be raised on <see cref="ReadOnlyDocument" />.</remarks>
|
|
||||||
public event EventHandler FileNameChanged { add { } remove { } } |
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public string FileName { |
|
||||||
get { return fileName; } |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,99 +0,0 @@ |
|||||||
// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
|
||||||
// software and associated documentation files (the "Software"), to deal in the Software
|
|
||||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
|
||||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
|
||||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all copies or
|
|
||||||
// substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
||||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
||||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
|
||||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
||||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
// DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
using System; |
|
||||||
using System.Windows.Documents; |
|
||||||
using ICSharpCode.Decompiler.Output; |
|
||||||
using ICSharpCode.Decompiler.TypeSystem; |
|
||||||
using ICSharpCode.NRefactory.TypeSystem; |
|
||||||
using ICSharpCode.NRefactory.Xml; |
|
||||||
|
|
||||||
namespace ICSharpCode.ILSpy.TextView |
|
||||||
{ |
|
||||||
/// <summary>
|
|
||||||
/// Provides helper methods to create nicely formatted FlowDocuments from NRefactory XmlDoc.
|
|
||||||
/// </summary>
|
|
||||||
public static class XmlDocFormatter |
|
||||||
{ |
|
||||||
public static FlowDocument CreateTooltip(IType type, bool useFullyQualifiedMemberNames = true) |
|
||||||
{ |
|
||||||
var ambience = AmbienceService.GetCurrentAmbience(); |
|
||||||
ambience.ConversionFlags = ConversionFlags.StandardConversionFlags | ConversionFlags.ShowDeclaringType; |
|
||||||
if (useFullyQualifiedMemberNames) |
|
||||||
ambience.ConversionFlags |= ConversionFlags.UseFullyQualifiedEntityNames; |
|
||||||
string header; |
|
||||||
if (type is ITypeDefinition) |
|
||||||
header = ambience.ConvertSymbol((ITypeDefinition)type); |
|
||||||
else |
|
||||||
header = ambience.ConvertType(type); |
|
||||||
|
|
||||||
ambience.ConversionFlags = ConversionFlags.ShowTypeParameterList; |
|
||||||
DocumentationUIBuilder b = new DocumentationUIBuilder(ambience); |
|
||||||
b.AddCodeBlock(header, keepLargeMargin: true); |
|
||||||
|
|
||||||
ITypeDefinition entity = type.GetDefinition();/* |
|
||||||
if (entity != null) { |
|
||||||
var documentation = XmlDocumentationElement.Get(entity); |
|
||||||
if (documentation != null) { |
|
||||||
foreach (var child in documentation.Children) { |
|
||||||
b.AddDocumentationElement(child); |
|
||||||
} |
|
||||||
} |
|
||||||
}*/ |
|
||||||
return b.CreateFlowDocument(); |
|
||||||
} |
|
||||||
|
|
||||||
public static FlowDocument CreateTooltip(IEntity entity, bool useFullyQualifiedMemberNames = true) |
|
||||||
{ |
|
||||||
var ambience = AmbienceService.GetCurrentAmbience(); |
|
||||||
ambience.ConversionFlags = ConversionFlags.StandardConversionFlags | ConversionFlags.ShowDeclaringType; |
|
||||||
if (useFullyQualifiedMemberNames) |
|
||||||
ambience.ConversionFlags |= ConversionFlags.UseFullyQualifiedEntityNames; |
|
||||||
string header = ambience.ConvertSymbol(entity); |
|
||||||
var documentation = XmlDocumentationElement.Get(entity); |
|
||||||
|
|
||||||
ambience.ConversionFlags = ConversionFlags.ShowTypeParameterList; |
|
||||||
DocumentationUIBuilder b = new DocumentationUIBuilder(ambience); |
|
||||||
b.AddCodeBlock(header, keepLargeMargin: true); |
|
||||||
if (documentation != null) { |
|
||||||
foreach (var child in documentation.Children) { |
|
||||||
b.AddDocumentationElement(child); |
|
||||||
} |
|
||||||
} |
|
||||||
return b.CreateFlowDocument(); |
|
||||||
} |
|
||||||
|
|
||||||
public static FlowDocument CreateTooltip(ISymbol symbol) |
|
||||||
{ |
|
||||||
var ambience = AmbienceService.GetCurrentAmbience(); |
|
||||||
ambience.ConversionFlags = ConversionFlags.StandardConversionFlags | ConversionFlags.ShowDeclaringType; |
|
||||||
string header = ambience.ConvertSymbol(symbol); |
|
||||||
|
|
||||||
if (symbol is IParameter) { |
|
||||||
header = "parameter " + header; |
|
||||||
} else if (symbol is IVariable) { |
|
||||||
header = "local variable " + header; |
|
||||||
} |
|
||||||
|
|
||||||
ambience.ConversionFlags = ConversionFlags.ShowTypeParameterList; |
|
||||||
DocumentationUIBuilder b = new DocumentationUIBuilder(ambience); |
|
||||||
b.AddCodeBlock(header, keepLargeMargin: true); |
|
||||||
return b.CreateFlowDocument(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,551 +0,0 @@ |
|||||||
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
|
||||||
// software and associated documentation files (the "Software"), to deal in the Software
|
|
||||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
|
||||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
|
||||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all copies or
|
|
||||||
// substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
||||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
||||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
|
||||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
||||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
// DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
using System; |
|
||||||
using System.Collections.Generic; |
|
||||||
using System.Diagnostics; |
|
||||||
using System.IO; |
|
||||||
using System.Linq; |
|
||||||
using System.Text; |
|
||||||
using System.Text.RegularExpressions; |
|
||||||
using System.Windows; |
|
||||||
using System.Windows.Controls; |
|
||||||
using System.Windows.Documents; |
|
||||||
using System.Windows.Media; |
|
||||||
using System.Xml; |
|
||||||
using System.Xml.Linq; |
|
||||||
using ICSharpCode.AvalonEdit.Document; |
|
||||||
using ICSharpCode.AvalonEdit.Highlighting; |
|
||||||
using ICSharpCode.AvalonEdit.Utils; |
|
||||||
using ICSharpCode.Decompiler.Documentation; |
|
||||||
using ICSharpCode.Decompiler.Output; |
|
||||||
using ICSharpCode.Decompiler.TypeSystem; |
|
||||||
using ICSharpCode.ILSpy.Options; |
|
||||||
|
|
||||||
namespace ICSharpCode.ILSpy.TextView |
|
||||||
{ |
|
||||||
/// <summary>
|
|
||||||
/// Renders XML documentation into a WPF <see cref="FlowDocument"/>.
|
|
||||||
/// </summary>
|
|
||||||
public class DocumentationUIBuilder |
|
||||||
{ |
|
||||||
readonly IAmbience ambience; |
|
||||||
readonly IHighlightingDefinition highlightingDefinition; |
|
||||||
readonly FlowDocument document; |
|
||||||
BlockCollection blockCollection; |
|
||||||
InlineCollection inlineCollection; |
|
||||||
|
|
||||||
public DocumentationUIBuilder(IAmbience ambience, IHighlightingDefinition highlightingDefinition) |
|
||||||
{ |
|
||||||
this.ambience = ambience; |
|
||||||
this.highlightingDefinition = highlightingDefinition; |
|
||||||
this.document = new FlowDocument(); |
|
||||||
this.blockCollection = document.Blocks; |
|
||||||
|
|
||||||
this.ShowSummary = true; |
|
||||||
this.ShowAllParameters = true; |
|
||||||
this.ShowReturns = true; |
|
||||||
this.ShowThreadSafety = true; |
|
||||||
this.ShowExceptions = true; |
|
||||||
this.ShowTypeParameters = true; |
|
||||||
|
|
||||||
this.ShowExample = true; |
|
||||||
this.ShowPreliminary = true; |
|
||||||
this.ShowSeeAlso = true; |
|
||||||
this.ShowValue = true; |
|
||||||
this.ShowPermissions = true; |
|
||||||
this.ShowRemarks = true; |
|
||||||
} |
|
||||||
|
|
||||||
public FlowDocument CreateDocument() |
|
||||||
{ |
|
||||||
FlushAddedText(true); |
|
||||||
return document; |
|
||||||
} |
|
||||||
|
|
||||||
public bool ShowExceptions { get; set; } |
|
||||||
public bool ShowPermissions { get; set; } |
|
||||||
public bool ShowExample { get; set; } |
|
||||||
public bool ShowPreliminary { get; set; } |
|
||||||
public bool ShowRemarks { get; set; } |
|
||||||
public bool ShowSummary { get; set; } |
|
||||||
public bool ShowReturns { get; set; } |
|
||||||
public bool ShowSeeAlso { get; set; } |
|
||||||
public bool ShowThreadSafety { get; set; } |
|
||||||
public bool ShowTypeParameters { get; set; } |
|
||||||
public bool ShowValue { get; set; } |
|
||||||
public bool ShowAllParameters { get; set; } |
|
||||||
|
|
||||||
public void AddCodeBlock(string textContent, bool keepLargeMargin = false) |
|
||||||
{ |
|
||||||
var document = new TextDocument(textContent); |
|
||||||
var highlighter = new DocumentHighlighter(document, highlightingDefinition); |
|
||||||
var richText = DocumentPrinter.ConvertTextDocumentToRichText(document, highlighter).ToRichTextModel(); |
|
||||||
|
|
||||||
var block = new Paragraph(); |
|
||||||
block.Inlines.AddRange(richText.CreateRuns(document)); |
|
||||||
block.FontFamily = GetCodeFont(); |
|
||||||
if (!keepLargeMargin) |
|
||||||
block.Margin = new Thickness(0, 6, 0, 6); |
|
||||||
AddBlock(block); |
|
||||||
} |
|
||||||
|
|
||||||
public void AddSignatureBlock(string signature, RichTextModel highlighting = null) |
|
||||||
{ |
|
||||||
var document = new TextDocument(signature); |
|
||||||
var richText = highlighting ?? DocumentPrinter.ConvertTextDocumentToRichText(document, new DocumentHighlighter(document, highlightingDefinition)).ToRichTextModel(); |
|
||||||
var block = new Paragraph(); |
|
||||||
// HACK: measure width of signature using a TextBlock
|
|
||||||
// Paragraph sadly does not support TextWrapping.NoWrap
|
|
||||||
var text = new TextBlock { |
|
||||||
FontFamily = GetCodeFont(), |
|
||||||
FontSize = DisplaySettingsPanel.CurrentDisplaySettings.SelectedFontSize, |
|
||||||
TextAlignment = TextAlignment.Left |
|
||||||
}; |
|
||||||
text.Inlines.AddRange(richText.CreateRuns(document)); |
|
||||||
text.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); |
|
||||||
this.document.MinPageWidth = Math.Min(text.DesiredSize.Width, MainWindow.Instance.ActualWidth); |
|
||||||
block.Inlines.AddRange(richText.CreateRuns(document)); |
|
||||||
block.FontFamily = GetCodeFont(); |
|
||||||
block.TextAlignment = TextAlignment.Left; |
|
||||||
AddBlock(block); |
|
||||||
} |
|
||||||
|
|
||||||
public void AddXmlDocumentation(string xmlDocumentation, IEntity declaringEntity, Func<string, IEntity> resolver) |
|
||||||
{ |
|
||||||
if (xmlDocumentation == null) |
|
||||||
return; |
|
||||||
Debug.WriteLine(xmlDocumentation); |
|
||||||
var xml = XElement.Parse("<doc>" + xmlDocumentation + "</doc>"); |
|
||||||
AddDocumentationElement(new XmlDocumentationElement(xml, declaringEntity, resolver)); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets/Sets the name of the parameter that should be shown.
|
|
||||||
/// </summary>
|
|
||||||
public string ParameterName { get; set; } |
|
||||||
|
|
||||||
public void AddDocumentationElement(XmlDocumentationElement element) |
|
||||||
{ |
|
||||||
if (element == null) |
|
||||||
throw new ArgumentNullException("element"); |
|
||||||
if (element.IsTextNode) { |
|
||||||
AddText(element.TextContent); |
|
||||||
return; |
|
||||||
} |
|
||||||
switch (element.Name) { |
|
||||||
case "b": |
|
||||||
AddSpan(new Bold(), element.Children); |
|
||||||
break; |
|
||||||
case "i": |
|
||||||
AddSpan(new Italic(), element.Children); |
|
||||||
break; |
|
||||||
case "c": |
|
||||||
AddSpan(new Span { FontFamily = GetCodeFont() }, element.Children); |
|
||||||
break; |
|
||||||
case "code": |
|
||||||
AddCodeBlock(element.TextContent); |
|
||||||
break; |
|
||||||
case "example": |
|
||||||
if (ShowExample) |
|
||||||
AddSection("Example: ", element.Children); |
|
||||||
break; |
|
||||||
case "exception": |
|
||||||
if (ShowExceptions) |
|
||||||
AddException(element.ReferencedEntity, element.Children); |
|
||||||
break; |
|
||||||
case "list": |
|
||||||
AddList(element.GetAttribute("type"), element.Children); |
|
||||||
break; |
|
||||||
//case "note":
|
|
||||||
// throw new NotImplementedException();
|
|
||||||
case "para": |
|
||||||
AddParagraph(new Paragraph { Margin = new Thickness(0, 5, 0, 5) }, element.Children); |
|
||||||
break; |
|
||||||
case "param": |
|
||||||
if (ShowAllParameters || (ParameterName != null && ParameterName == element.GetAttribute("name"))) |
|
||||||
AddParam(element.GetAttribute("name"), element.Children); |
|
||||||
break; |
|
||||||
case "paramref": |
|
||||||
AddParamRef(element.GetAttribute("name")); |
|
||||||
break; |
|
||||||
case "permission": |
|
||||||
if (ShowPermissions) |
|
||||||
AddPermission(element.ReferencedEntity, element.Children); |
|
||||||
break; |
|
||||||
case "preliminary": |
|
||||||
if (ShowPreliminary) |
|
||||||
AddPreliminary(element.Children); |
|
||||||
break; |
|
||||||
case "remarks": |
|
||||||
if (ShowRemarks) |
|
||||||
AddSection("Remarks: ", element.Children); |
|
||||||
break; |
|
||||||
case "returns": |
|
||||||
if (ShowReturns) |
|
||||||
AddSection("Returns: ", element.Children); |
|
||||||
break; |
|
||||||
case "see": |
|
||||||
AddSee(element); |
|
||||||
break; |
|
||||||
case "seealso": |
|
||||||
if (inlineCollection != null) |
|
||||||
AddSee(element); |
|
||||||
else if (ShowSeeAlso) |
|
||||||
AddSection(new Run("See also: "), () => AddSee(element)); |
|
||||||
break; |
|
||||||
case "summary": |
|
||||||
if (ShowSummary) |
|
||||||
AddSection("Summary: ", element.Children); |
|
||||||
break; |
|
||||||
case "threadsafety": |
|
||||||
if (ShowThreadSafety) |
|
||||||
AddThreadSafety(ParseBool(element.GetAttribute("static")), ParseBool(element.GetAttribute("instance")), element.Children); |
|
||||||
break; |
|
||||||
case "typeparam": |
|
||||||
if (ShowTypeParameters) |
|
||||||
AddSection("Type parameter " + element.GetAttribute("name") + ": ", element.Children); |
|
||||||
break; |
|
||||||
case "typeparamref": |
|
||||||
AddText(element.GetAttribute("name")); |
|
||||||
break; |
|
||||||
case "value": |
|
||||||
if (ShowValue) |
|
||||||
AddSection("Value: ", element.Children); |
|
||||||
break; |
|
||||||
case "exclude": |
|
||||||
case "filterpriority": |
|
||||||
case "overloads": |
|
||||||
// ignore children
|
|
||||||
break; |
|
||||||
case "br": |
|
||||||
AddLineBreak(); |
|
||||||
break; |
|
||||||
default: |
|
||||||
foreach (var child in element.Children) |
|
||||||
AddDocumentationElement(child); |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void AddList(string type, IEnumerable<XmlDocumentationElement> items) |
|
||||||
{ |
|
||||||
List list = new List(); |
|
||||||
AddBlock(list); |
|
||||||
list.Margin = new Thickness(0, 5, 0, 5); |
|
||||||
if (type == "number") |
|
||||||
list.MarkerStyle = TextMarkerStyle.Decimal; |
|
||||||
else if (type == "bullet") |
|
||||||
list.MarkerStyle = TextMarkerStyle.Disc; |
|
||||||
var oldBlockCollection = blockCollection; |
|
||||||
try { |
|
||||||
foreach (var itemElement in items) { |
|
||||||
if (itemElement.Name == "listheader" || itemElement.Name == "item") { |
|
||||||
ListItem item = new ListItem(); |
|
||||||
blockCollection = item.Blocks; |
|
||||||
inlineCollection = null; |
|
||||||
foreach (var prop in itemElement.Children) { |
|
||||||
AddDocumentationElement(prop); |
|
||||||
} |
|
||||||
FlushAddedText(false); |
|
||||||
list.ListItems.Add(item); |
|
||||||
} |
|
||||||
} |
|
||||||
} finally { |
|
||||||
blockCollection = oldBlockCollection; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
bool? ParseBool(string input) |
|
||||||
{ |
|
||||||
if (bool.TryParse(input, out bool result)) |
|
||||||
return result; |
|
||||||
else |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
void AddThreadSafety(bool? staticThreadSafe, bool? instanceThreadSafe, IEnumerable<XmlDocumentationElement> children) |
|
||||||
{ |
|
||||||
AddSection( |
|
||||||
new Run("Thread-safety: "), |
|
||||||
delegate { |
|
||||||
if (staticThreadSafe == true) |
|
||||||
AddText("Any public static members of this type are thread safe. "); |
|
||||||
else if (staticThreadSafe == false) |
|
||||||
AddText("The static members of this type are not thread safe. "); |
|
||||||
|
|
||||||
if (instanceThreadSafe == true) |
|
||||||
AddText("Any public instance members of this type are thread safe. "); |
|
||||||
else if (instanceThreadSafe == false) |
|
||||||
AddText("Any instance members are not guaranteed to be thread safe. "); |
|
||||||
|
|
||||||
foreach (var child in children) |
|
||||||
AddDocumentationElement(child); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
void AddException(IEntity referencedEntity, IList<XmlDocumentationElement> children) |
|
||||||
{ |
|
||||||
Span span = new Span(); |
|
||||||
if (referencedEntity != null) |
|
||||||
span.Inlines.Add(ConvertReference(referencedEntity)); |
|
||||||
else |
|
||||||
span.Inlines.Add("Exception"); |
|
||||||
span.Inlines.Add(": "); |
|
||||||
AddSection(span, children); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
void AddPermission(IEntity referencedEntity, IList<XmlDocumentationElement> children) |
|
||||||
{ |
|
||||||
Span span = new Span(); |
|
||||||
span.Inlines.Add("Permission"); |
|
||||||
if (referencedEntity != null) { |
|
||||||
span.Inlines.Add(" "); |
|
||||||
span.Inlines.Add(ConvertReference(referencedEntity)); |
|
||||||
} |
|
||||||
span.Inlines.Add(": "); |
|
||||||
AddSection(span, children); |
|
||||||
} |
|
||||||
|
|
||||||
Inline ConvertReference(IEntity referencedEntity) |
|
||||||
{ |
|
||||||
var h = new Hyperlink(new Run(ambience.ConvertSymbol(referencedEntity))); |
|
||||||
h.Click += (sender, e) => { |
|
||||||
MainWindow.Instance.JumpToReference(referencedEntity); |
|
||||||
}; |
|
||||||
return h; |
|
||||||
} |
|
||||||
|
|
||||||
void AddParam(string name, IEnumerable<XmlDocumentationElement> children) |
|
||||||
{ |
|
||||||
Span span = new Span(); |
|
||||||
span.Inlines.Add(new Run(name ?? string.Empty) { FontStyle = FontStyles.Italic }); |
|
||||||
span.Inlines.Add(": "); |
|
||||||
AddSection(span, children); |
|
||||||
} |
|
||||||
|
|
||||||
void AddParamRef(string name) |
|
||||||
{ |
|
||||||
if (name != null) { |
|
||||||
AddInline(new Run(name) { FontStyle = FontStyles.Italic }); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void AddPreliminary(IEnumerable<XmlDocumentationElement> children) |
|
||||||
{ |
|
||||||
if (children.Any()) { |
|
||||||
foreach (var child in children) |
|
||||||
AddDocumentationElement(child); |
|
||||||
} else { |
|
||||||
AddText("[This is preliminary documentation and subject to change.]"); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void AddSee(XmlDocumentationElement element) |
|
||||||
{ |
|
||||||
IEntity referencedEntity = element.ReferencedEntity; |
|
||||||
if (referencedEntity != null) { |
|
||||||
if (element.Children.Any()) { |
|
||||||
Hyperlink link = new Hyperlink(); |
|
||||||
link.Click += (sender, e) => { |
|
||||||
MainWindow.Instance.JumpToReference(referencedEntity); |
|
||||||
}; |
|
||||||
AddSpan(link, element.Children); |
|
||||||
} else { |
|
||||||
AddInline(ConvertReference(referencedEntity)); |
|
||||||
} |
|
||||||
} else if (element.GetAttribute("langword") != null) { |
|
||||||
AddInline(new Run(element.GetAttribute("langword")) { FontFamily = GetCodeFont() }); |
|
||||||
} else if (element.GetAttribute("href") != null) { |
|
||||||
Uri uri; |
|
||||||
if (Uri.TryCreate(element.GetAttribute("href"), UriKind.Absolute, out uri)) { |
|
||||||
if (element.Children.Any()) { |
|
||||||
AddSpan(new Hyperlink { NavigateUri = uri }, element.Children); |
|
||||||
} else { |
|
||||||
AddInline(new Hyperlink(new Run(element.GetAttribute("href"))) { NavigateUri = uri }); |
|
||||||
} |
|
||||||
} |
|
||||||
} else { |
|
||||||
// Invalid reference: print the cref value
|
|
||||||
AddText(element.GetAttribute("cref")); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
static string GetCref(string cref) |
|
||||||
{ |
|
||||||
if (cref == null || cref.Trim().Length==0) { |
|
||||||
return ""; |
|
||||||
} |
|
||||||
if (cref.Length < 2) { |
|
||||||
return cref; |
|
||||||
} |
|
||||||
if (cref.Substring(1, 1) == ":") { |
|
||||||
return cref.Substring(2, cref.Length - 2); |
|
||||||
} |
|
||||||
return cref; |
|
||||||
} |
|
||||||
|
|
||||||
FontFamily GetCodeFont() |
|
||||||
{ |
|
||||||
return DisplaySettingsPanel.CurrentDisplaySettings.SelectedFont; |
|
||||||
} |
|
||||||
|
|
||||||
public void AddInline(Inline inline) |
|
||||||
{ |
|
||||||
FlushAddedText(false); |
|
||||||
if (inlineCollection == null) { |
|
||||||
var para = new Paragraph(); |
|
||||||
para.Margin = new Thickness(0, 0, 0, 5); |
|
||||||
inlineCollection = para.Inlines; |
|
||||||
AddBlock(para); |
|
||||||
} |
|
||||||
inlineCollection.Add(inline); |
|
||||||
ignoreWhitespace = false; |
|
||||||
} |
|
||||||
|
|
||||||
void AddSection(string title, IEnumerable<XmlDocumentationElement> children) |
|
||||||
{ |
|
||||||
AddSection(new Run(title), children); |
|
||||||
} |
|
||||||
|
|
||||||
void AddSection(Inline title, IEnumerable<XmlDocumentationElement> children) |
|
||||||
{ |
|
||||||
AddSection( |
|
||||||
title, delegate { |
|
||||||
foreach (var child in children) |
|
||||||
AddDocumentationElement(child); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
void AddSection(Inline title, Action addChildren) |
|
||||||
{ |
|
||||||
var section = new Section(); |
|
||||||
AddBlock(section); |
|
||||||
var oldBlockCollection = blockCollection; |
|
||||||
try { |
|
||||||
blockCollection = section.Blocks; |
|
||||||
inlineCollection = null; |
|
||||||
|
|
||||||
if (title != null) |
|
||||||
AddInline(new Bold(title)); |
|
||||||
|
|
||||||
addChildren(); |
|
||||||
FlushAddedText(false); |
|
||||||
} finally { |
|
||||||
blockCollection = oldBlockCollection; |
|
||||||
inlineCollection = null; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void AddParagraph(Paragraph para, IEnumerable<XmlDocumentationElement> children) |
|
||||||
{ |
|
||||||
AddBlock(para); |
|
||||||
try { |
|
||||||
inlineCollection = para.Inlines; |
|
||||||
|
|
||||||
foreach (var child in children) |
|
||||||
AddDocumentationElement(child); |
|
||||||
FlushAddedText(false); |
|
||||||
} finally { |
|
||||||
inlineCollection = null; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void AddSpan(Span span, IEnumerable<XmlDocumentationElement> children) |
|
||||||
{ |
|
||||||
AddInline(span); |
|
||||||
var oldInlineCollection = inlineCollection; |
|
||||||
try { |
|
||||||
inlineCollection = span.Inlines; |
|
||||||
foreach (var child in children) |
|
||||||
AddDocumentationElement(child); |
|
||||||
FlushAddedText(false); |
|
||||||
} finally { |
|
||||||
inlineCollection = oldInlineCollection; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public void AddBlock(Block block) |
|
||||||
{ |
|
||||||
FlushAddedText(true); |
|
||||||
blockCollection.Add(block); |
|
||||||
} |
|
||||||
|
|
||||||
StringBuilder addedText = new StringBuilder(); |
|
||||||
bool ignoreWhitespace; |
|
||||||
|
|
||||||
public void AddLineBreak() |
|
||||||
{ |
|
||||||
TrimEndOfAddedText(); |
|
||||||
addedText.AppendLine(); |
|
||||||
ignoreWhitespace = true; |
|
||||||
} |
|
||||||
|
|
||||||
public void AddText(string textContent) |
|
||||||
{ |
|
||||||
if (string.IsNullOrEmpty(textContent)) |
|
||||||
return; |
|
||||||
for (int i = 0; i < textContent.Length; i++) { |
|
||||||
char c = textContent[i]; |
|
||||||
if (c == '\n' && IsEmptyLineBefore(textContent, i)) { |
|
||||||
AddLineBreak(); // empty line -> line break
|
|
||||||
} else if (char.IsWhiteSpace(c)) { |
|
||||||
// any whitespace sequence gets converted to a single space (like HTML)
|
|
||||||
if (!ignoreWhitespace) { |
|
||||||
addedText.Append(' '); |
|
||||||
ignoreWhitespace = true; |
|
||||||
} |
|
||||||
} else { |
|
||||||
addedText.Append(c); |
|
||||||
ignoreWhitespace = false; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
bool IsEmptyLineBefore(string text, int i) |
|
||||||
{ |
|
||||||
// Skip previous whitespace
|
|
||||||
do { |
|
||||||
i--; |
|
||||||
} while (i >= 0 && (text[i] == ' ' || text[i] == '\r')); |
|
||||||
// Check if previous non-whitespace char is \n
|
|
||||||
return i >= 0 && text[i] == '\n'; |
|
||||||
} |
|
||||||
|
|
||||||
void TrimEndOfAddedText() |
|
||||||
{ |
|
||||||
while (addedText.Length > 0 && addedText[addedText.Length - 1] == ' ') { |
|
||||||
addedText.Length--; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void FlushAddedText(bool trim) |
|
||||||
{ |
|
||||||
if (trim) // trim end of current text element
|
|
||||||
TrimEndOfAddedText(); |
|
||||||
if (addedText.Length == 0) |
|
||||||
return; |
|
||||||
string text = addedText.ToString(); |
|
||||||
addedText.Length = 0; |
|
||||||
AddInline(new Run(text)); |
|
||||||
ignoreWhitespace = trim; // trim start of next text element
|
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
Loading…
Reference in new issue