24 changed files with 719 additions and 187 deletions
@ -0,0 +1,438 @@
@@ -0,0 +1,438 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.IO; |
||||
using System.Linq; |
||||
using System.Windows; |
||||
using System.Windows.Documents; |
||||
using System.Windows.Media; |
||||
using ICSharpCode.AvalonEdit.AddIn.Options; |
||||
using ICSharpCode.AvalonEdit.Highlighting; |
||||
using ICSharpCode.NRefactory.Editor; |
||||
using ICSharpCode.NRefactory.TypeSystem; |
||||
using ICSharpCode.NRefactory.Xml; |
||||
using ICSharpCode.SharpDevelop; |
||||
|
||||
namespace ICSharpCode.AvalonEdit.AddIn.XmlDoc |
||||
{ |
||||
/// <summary>
|
||||
/// Builds a FlowDocument for XML documentation.
|
||||
/// </summary>
|
||||
public class DocumentationUIBuilder |
||||
{ |
||||
FlowDocument flowDocument; |
||||
BlockCollection blockCollection; |
||||
InlineCollection inlineCollection; |
||||
IAmbience ambience; |
||||
|
||||
public DocumentationUIBuilder(IAmbience ambience = null) |
||||
{ |
||||
this.ambience = ambience ?? AmbienceService.GetCurrentAmbience(); |
||||
this.flowDocument = new FlowDocument(); |
||||
this.blockCollection = flowDocument.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 FlowDocument { |
||||
get { return flowDocument; } |
||||
} |
||||
|
||||
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; } |
||||
|
||||
/// <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; |
||||
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; |
||||
} |
||||
} |
||||
|
||||
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); |
||||
} |
||||
|
||||
bool? ParseBool(string input) |
||||
{ |
||||
bool result; |
||||
if (bool.TryParse(input, out 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); |
||||
}); |
||||
} |
||||
|
||||
FontFamily GetCodeFont() |
||||
{ |
||||
return new FontFamily(CodeEditorOptions.Instance.FontFamily); |
||||
} |
||||
|
||||
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.ConvertEntity(referencedEntity))); |
||||
h.Click += delegate(object sender, RoutedEventArgs e) { |
||||
SharpDevelop.NavigationService.NavigateTo(referencedEntity); |
||||
e.Handled = true; |
||||
}; |
||||
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 += delegate(object sender, RoutedEventArgs e) { |
||||
SharpDevelop.NavigationService.NavigateTo(referencedEntity); |
||||
e.Handled = true; |
||||
}; |
||||
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")); |
||||
} |
||||
} |
||||
|
||||
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(); |
||||
blockCollection.Add(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) |
||||
{ |
||||
blockCollection.Add(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 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); |
||||
} |
||||
|
||||
public void AddBlock(Block block) |
||||
{ |
||||
FlushAddedText(true); |
||||
blockCollection.Add(block); |
||||
} |
||||
|
||||
string addedText; |
||||
|
||||
public void AddText(string textContent) |
||||
{ |
||||
if (string.IsNullOrEmpty(textContent)) |
||||
return; |
||||
if (inlineCollection == null && string.IsNullOrWhiteSpace(textContent)) |
||||
return; |
||||
FlushAddedText(false); |
||||
addedText = textContent; |
||||
} |
||||
|
||||
void FlushAddedText(bool trimEnd) |
||||
{ |
||||
if (addedText == null) |
||||
return; |
||||
string text = addedText; |
||||
addedText = null; |
||||
AddInline(new Run(trimEnd ? text.TrimEnd() : text)); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,87 @@
@@ -0,0 +1,87 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.Windows; |
||||
using System.Windows.Controls; |
||||
using System.Windows.Documents; |
||||
using System.Windows.Media; |
||||
using ICSharpCode.AvalonEdit.AddIn.Options; |
||||
using ICSharpCode.NRefactory.Semantics; |
||||
using ICSharpCode.NRefactory.TypeSystem; |
||||
using ICSharpCode.NRefactory.Xml; |
||||
using ICSharpCode.SharpDevelop; |
||||
using ICSharpCode.SharpDevelop.Editor; |
||||
|
||||
namespace ICSharpCode.AvalonEdit.AddIn.XmlDoc |
||||
{ |
||||
public class XmlDocTooltipProvider : ITextAreaToolTipProvider |
||||
{ |
||||
public void HandleToolTipRequest(ToolTipRequestEventArgs e) |
||||
{ |
||||
if (e.ResolveResult == null) |
||||
return; |
||||
TypeResolveResult trr = e.ResolveResult as TypeResolveResult; |
||||
MemberResolveResult mrr = e.ResolveResult as MemberResolveResult; |
||||
LocalResolveResult lrr = e.ResolveResult as LocalResolveResult; |
||||
if (trr != null && trr.Type.GetDefinition() != null) { |
||||
e.SetToolTip(CreateTooltip(trr.Type.GetDefinition())); |
||||
} else if (mrr != null) { |
||||
e.SetToolTip(CreateTooltip(mrr.Member)); |
||||
} else if (lrr != null) { |
||||
var ambience = AmbienceService.GetCurrentAmbience(); |
||||
e.SetToolTip(ambience.ConvertVariable(lrr.Variable)); |
||||
} |
||||
} |
||||
|
||||
sealed class FlowDocumentTooltip : Border, ITooltip |
||||
{ |
||||
FlowDocumentScrollViewer viewer; |
||||
|
||||
public FlowDocumentTooltip(FlowDocument document) |
||||
{ |
||||
viewer = new FlowDocumentScrollViewer(); |
||||
viewer.Document = document; |
||||
this.Child = viewer; |
||||
|
||||
this.Background = SystemColors.InfoBrush; |
||||
viewer.Foreground = SystemColors.InfoTextBrush; |
||||
this.BorderBrush = SystemColors.InfoTextBrush; |
||||
this.BorderThickness = new Thickness(1); |
||||
this.MaxHeight = 400; |
||||
document.FontSize = CodeEditorOptions.Instance.FontSize; |
||||
} |
||||
|
||||
public event RoutedEventHandler Closed { add {} remove {} } |
||||
|
||||
public bool ShowAsPopup { |
||||
get { return true; } |
||||
} |
||||
|
||||
public bool Close(bool mouseClick) |
||||
{ |
||||
return true; |
||||
} |
||||
} |
||||
|
||||
object CreateTooltip(IEntity entity) |
||||
{ |
||||
var ambience = AmbienceService.GetCurrentAmbience(); |
||||
ambience.ConversionFlags = ConversionFlags.StandardConversionFlags; |
||||
string header = ambience.ConvertEntity(entity); |
||||
var documentation = XmlDocumentationElement.Get(entity); |
||||
|
||||
if (documentation != null) { |
||||
ambience.ConversionFlags = ConversionFlags.ShowTypeParameterList; |
||||
DocumentationUIBuilder b = new DocumentationUIBuilder(ambience); |
||||
b.AddCodeBlock(header, keepLargeMargin: true); |
||||
foreach (var child in documentation.Children) { |
||||
b.AddDocumentationElement(child); |
||||
} |
||||
return new FlowDocumentTooltip(b.FlowDocument); |
||||
} else { |
||||
return header; |
||||
} |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue