diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index 8c95cf49c..90607e652 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -226,8 +226,7 @@ - - + diff --git a/ILSpy/TextView/DocumentationUIBuilder.cs b/ILSpy/TextView/DocumentationUIBuilder.cs index 4451ba51a..14cd8c421 100644 --- a/ILSpy/TextView/DocumentationUIBuilder.cs +++ b/ILSpy/TextView/DocumentationUIBuilder.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team +// 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 @@ -18,16 +18,21 @@ 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.CSharp.OutputVisitor; +using ICSharpCode.Decompiler.Documentation; using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.Options; @@ -35,20 +40,22 @@ using ICSharpCode.ILSpy.Options; namespace ICSharpCode.ILSpy.TextView { /// - /// Builds a FlowDocument for XML documentation. + /// Renders XML documentation into a WPF . /// public class DocumentationUIBuilder { - FlowDocument flowDocument; + readonly IAmbience ambience; + readonly IHighlightingDefinition highlightingDefinition; + readonly FlowDocument document; BlockCollection blockCollection; InlineCollection inlineCollection; - IAmbience ambience; - public DocumentationUIBuilder(IAmbience ambience) + public DocumentationUIBuilder(IAmbience ambience, IHighlightingDefinition highlightingDefinition) { this.ambience = ambience; - this.flowDocument = new FlowDocument(); - this.blockCollection = flowDocument.Blocks; + this.highlightingDefinition = highlightingDefinition; + this.document = new FlowDocument(); + this.blockCollection = document.Blocks; this.ShowSummary = true; this.ShowAllParameters = true; @@ -65,11 +72,10 @@ namespace ICSharpCode.ILSpy.TextView this.ShowRemarks = true; } - public FlowDocument CreateFlowDocument() + public FlowDocument CreateDocument() { FlushAddedText(true); - flowDocument.FontSize = DisplaySettingsPanel.CurrentDisplaySettings.SelectedFontSize; - return flowDocument; + return document; } public bool ShowExceptions { get; set; } @@ -85,33 +91,76 @@ namespace ICSharpCode.ILSpy.TextView 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 resolver) + { + if (xmlDocumentation == null) + return; + Debug.WriteLine(xmlDocumentation); + var xml = XElement.Parse("" + xmlDocumentation + ""); + AddDocumentationElement(new XmlDocumentationElement(xml, declaringEntity, resolver)); + } + + /// /// Gets/Sets the name of the parameter that should be shown. /// public string ParameterName { get; set; } - public void AddDocumentationElement(XNode node) + public void AddDocumentationElement(XmlDocumentationElement element) { - if (node == null) - throw new ArgumentNullException(nameof(node)); - if (node is XText text) { - AddText(text.Value); + if (element == null) + throw new ArgumentNullException("element"); + if (element.IsTextNode) { + AddText(element.TextContent); return; } - if (!(node is XElement element)) - throw new NotImplementedException(); - switch (element.Name.ToString()) { + switch (element.Name) { case "b": - AddSpan(new Bold(), element.Elements()); + AddSpan(new Bold(), element.Children); break; case "i": - AddSpan(new Italic(), element.ele); + AddSpan(new Italic(), element.Children); break; case "c": AddSpan(new Span { FontFamily = GetCodeFont() }, element.Children); break; case "code": - AddCodeBlock(element.Value); + AddCodeBlock(element.TextContent); break; case "example": if (ShowExample) @@ -195,7 +244,7 @@ namespace ICSharpCode.ILSpy.TextView } } - void AddList(string type, IEnumerable items) + void AddList(string type, IEnumerable items) { List list = new List(); AddBlock(list); @@ -223,43 +272,15 @@ namespace ICSharpCode.ILSpy.TextView } } - public void AddCodeBlock(string textContent, bool keepLargeMargin = false) - { - var document = new ReadOnlyDocument(textContent); - var highlightingDefinition = HighlightingManager.Instance.GetDefinition("C#"); - - var block = DocumentPrinter.ConvertTextDocumentToBlock(document, highlightingDefinition); - block.FontFamily = GetCodeFont(); - if (!keepLargeMargin) - block.Margin = new Thickness(0, 6, 0, 6); - AddBlock(block); - } - - public void AddSignatureBlock(string signature, int currentParameterOffset, int currentParameterLength, string currentParameterName) - { - ParameterName = currentParameterName; - var document = new ReadOnlyDocument(signature); - var highlightingDefinition = HighlightingManager.Instance.GetDefinition("C#"); - - var richText = DocumentPrinter.ConvertTextDocumentToRichText(document, highlightingDefinition).ToRichTextModel(); - richText.SetFontWeight(currentParameterOffset, currentParameterLength, FontWeights.Bold); - var block = new Paragraph(); - block.Inlines.AddRange(richText.CreateRuns(document)); - block.FontFamily = GetCodeFont(); - block.TextAlignment = TextAlignment.Left; - AddBlock(block); - } - bool? ParseBool(string input) { - bool result; - if (bool.TryParse(input, out result)) + if (bool.TryParse(input, out bool result)) return result; else return null; } - void AddThreadSafety(bool? staticThreadSafe, bool? instanceThreadSafe, IEnumerable children) + void AddThreadSafety(bool? staticThreadSafe, bool? instanceThreadSafe, IEnumerable children) { AddSection( new Run("Thread-safety: "), @@ -279,12 +300,7 @@ namespace ICSharpCode.ILSpy.TextView }); } - FontFamily GetCodeFont() - { - return new FontFamily(SD.EditorControlService.GlobalOptions.FontFamily); - } - - void AddException(IEntity referencedEntity, IList children) + void AddException(IEntity referencedEntity, IList children) { Span span = new Span(); if (referencedEntity != null) @@ -296,7 +312,7 @@ namespace ICSharpCode.ILSpy.TextView } - void AddPermission(IEntity referencedEntity, IList children) + void AddPermission(IEntity referencedEntity, IList children) { Span span = new Span(); span.Inlines.Add("Permission"); @@ -311,11 +327,13 @@ namespace ICSharpCode.ILSpy.TextView Inline ConvertReference(IEntity referencedEntity) { var h = new Hyperlink(new Run(ambience.ConvertSymbol(referencedEntity))); - h.Click += CreateNavigateOnClickHandler(referencedEntity); + h.Click += (sender, e) => { + MainWindow.Instance.JumpToReference(referencedEntity); + }; return h; } - void AddParam(string name, IEnumerable children) + void AddParam(string name, IEnumerable children) { Span span = new Span(); span.Inlines.Add(new Run(name ?? string.Empty) { FontStyle = FontStyles.Italic }); @@ -330,7 +348,7 @@ namespace ICSharpCode.ILSpy.TextView } } - void AddPreliminary(IEnumerable children) + void AddPreliminary(IEnumerable children) { if (children.Any()) { foreach (var child in children) @@ -340,13 +358,15 @@ namespace ICSharpCode.ILSpy.TextView } } - void AddSee(XNode element) + void AddSee(XmlDocumentationElement element) { IEntity referencedEntity = element.ReferencedEntity; if (referencedEntity != null) { if (element.Children.Any()) { Hyperlink link = new Hyperlink(); - link.Click += CreateNavigateOnClickHandler(referencedEntity); + link.Click += (sender, e) => { + MainWindow.Instance.JumpToReference(referencedEntity); + }; AddSpan(link, element.Children); } else { AddInline(ConvertReference(referencedEntity)); @@ -368,30 +388,44 @@ namespace ICSharpCode.ILSpy.TextView } } - RoutedEventHandler CreateNavigateOnClickHandler(IEntity referencedEntity) + static string GetCref(string cref) { - // Don't let the anonymous method capture the referenced entity - // (we don't want to keep the whole compilation in memory) - // Use the IEntityModel instead. - var model = referencedEntity.GetModel(); - return delegate (object sender, RoutedEventArgs e) { - IEntity resolvedEntity = model != null ? model.Resolve() : null; - if (resolvedEntity != null) { - bool shouldDisplayHelp = CodeCompletionOptions.TooltipLinkTarget == TooltipLinkTarget.Documentation - && resolvedEntity.ParentAssembly.IsPartOfDotnetFramework(); - if (!shouldDisplayHelp || !HelpProvider.ShowHelp(resolvedEntity)) - NavigationService.NavigateTo(resolvedEntity); - } - e.Handled = true; - }; + 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 children) + void AddSection(string title, IEnumerable children) { AddSection(new Run(title), children); } - void AddSection(Inline title, IEnumerable children) + void AddSection(Inline title, IEnumerable children) { AddSection( title, delegate { @@ -420,7 +454,7 @@ namespace ICSharpCode.ILSpy.TextView } } - void AddParagraph(Paragraph para, IEnumerable children) + void AddParagraph(Paragraph para, IEnumerable children) { AddBlock(para); try { @@ -434,7 +468,7 @@ namespace ICSharpCode.ILSpy.TextView } } - void AddSpan(Span span, IEnumerable children) + void AddSpan(Span span, IEnumerable children) { AddInline(span); var oldInlineCollection = inlineCollection; @@ -448,19 +482,6 @@ namespace ICSharpCode.ILSpy.TextView } } - 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; - } - public void AddBlock(Block block) { FlushAddedText(true); diff --git a/ILSpy/TextView/ReadOnlyDocument.cs b/ILSpy/TextView/ReadOnlyDocument.cs deleted file mode 100644 index 5a11ee298..000000000 --- a/ILSpy/TextView/ReadOnlyDocument.cs +++ /dev/null @@ -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 -{ - /// - /// Read-only implementation of . - /// - [Serializable] - public sealed class ReadOnlyDocument : IDocument - { - readonly ITextSource textSource; - readonly string fileName; - int[] lines; - - static readonly char[] newline = { '\r', '\n' }; - - /// - /// Creates a new ReadOnlyDocument from the given text source. - /// - public ReadOnlyDocument(ITextSource textSource) - { - if (textSource == null) - throw new ArgumentNullException("textSource"); - // ensure that underlying buffer is immutable - this.textSource = textSource.CreateSnapshot(); - List lines = new List(); - 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(); - } - - /// - /// Creates a new ReadOnlyDocument from the given string. - /// - public ReadOnlyDocument(string text) - : this(new StringTextSource(text)) - { - } - - /// - /// Creates a new ReadOnlyDocument from the given text source; - /// and sets IDocument.FileName to the specified file name. - /// - public ReadOnlyDocument(ITextSource textSource, string fileName) - : this(textSource) - { - this.fileName = fileName; - } - - /// - 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; - } - - /// - 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; - } - - /// - 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; - } - - /// - public int GetOffset(TextLocation location) - { - return GetOffset(location.Line, location.Column); - } - - /// - 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); - } - - /// - public string Text { - get { return textSource.Text; } - set { - throw new NotSupportedException(); - } - } - - /// - public int LineCount { - get { return lines.Length; } - } - - /// - public ITextSourceVersion Version { - get { return textSource.Version; } - } - - /// - public int TextLength { - get { return textSource.TextLength; } - } - - event EventHandler IDocument.TextChanging { add { } remove { } } - - event EventHandler 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; - } - - /// - 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; } - } - } - - /// - public ITextSource CreateSnapshot() - { - return textSource; // textBuffer is immutable - } - - /// - public ITextSource CreateSnapshot(int offset, int length) - { - return textSource.CreateSnapshot(offset, length); - } - - /// - public IDocument CreateDocumentSnapshot() - { - return this; // ReadOnlyDocument is immutable - } - - /// - public System.IO.TextReader CreateReader() - { - return textSource.CreateReader(); - } - - /// - public System.IO.TextReader CreateReader(int offset, int length) - { - return textSource.CreateReader(offset, length); - } - - /// - public void WriteTextTo(System.IO.TextWriter writer) - { - textSource.WriteTextTo(writer); - } - - /// - public void WriteTextTo(System.IO.TextWriter writer, int offset, int length) - { - textSource.WriteTextTo(writer, offset, length); - } - - /// - public char GetCharAt(int offset) - { - return textSource.GetCharAt(offset); - } - - /// - public string GetText(int offset, int length) - { - return textSource.GetText(offset, length); - } - - /// - public string GetText(ISegment segment) - { - return textSource.GetText(segment); - } - - /// - public int IndexOf(char c, int startIndex, int count) - { - return textSource.IndexOf(c, startIndex, count); - } - - /// - public int IndexOfAny(char[] anyOf, int startIndex, int count) - { - return textSource.IndexOfAny(anyOf, startIndex, count); - } - - /// - public int IndexOf(string searchText, int startIndex, int count, StringComparison comparisonType) - { - return textSource.IndexOf(searchText, startIndex, count, comparisonType); - } - - /// - public int LastIndexOf(char c, int startIndex, int count) - { - return textSource.LastIndexOf(c, startIndex, count); - } - - /// - 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; - } - - /// - /// Will never be raised on . - public event EventHandler FileNameChanged { add { } remove { } } - - /// - public string FileName { - get { return fileName; } - } - } -} diff --git a/ILSpy/TextView/XmlDocFormatter.cs b/ILSpy/TextView/XmlDocFormatter.cs deleted file mode 100644 index 37b80ab9b..000000000 --- a/ILSpy/TextView/XmlDocFormatter.cs +++ /dev/null @@ -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 -{ - /// - /// Provides helper methods to create nicely formatted FlowDocuments from NRefactory XmlDoc. - /// - 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(); - } - } -} diff --git a/ILSpy/TextView/XmlDocRenderer.cs b/ILSpy/TextView/XmlDocRenderer.cs deleted file mode 100644 index 14cd8c421..000000000 --- a/ILSpy/TextView/XmlDocRenderer.cs +++ /dev/null @@ -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 -{ - /// - /// Renders XML documentation into a WPF . - /// - 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 resolver) - { - if (xmlDocumentation == null) - return; - Debug.WriteLine(xmlDocumentation); - var xml = XElement.Parse("" + xmlDocumentation + ""); - AddDocumentationElement(new XmlDocumentationElement(xml, declaringEntity, resolver)); - } - - - /// - /// Gets/Sets the name of the parameter that should be shown. - /// - 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 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 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 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 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 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 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 children) - { - AddSection(new Run(title), children); - } - - void AddSection(Inline title, IEnumerable 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 children) - { - AddBlock(para); - try { - inlineCollection = para.Inlines; - - foreach (var child in children) - AddDocumentationElement(child); - FlushAddedText(false); - } finally { - inlineCollection = null; - } - } - - void AddSpan(Span span, IEnumerable 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 - } - } -}