diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs
index 32b257eda..4d5d62d76 100644
--- a/ICSharpCode.Decompiler/DecompilerSettings.cs
+++ b/ICSharpCode.Decompiler/DecompilerSettings.cs
@@ -209,6 +209,21 @@ namespace ICSharpCode.Decompiler
}
}
+ bool showXmlDocumentation = true;
+
+ ///
+ /// Gets/Sets whether to include XML documentation comments in the decompiled code
+ ///
+ public bool ShowXmlDocumentation {
+ get { return showXmlDocumentation; }
+ set {
+ if (showXmlDocumentation != value) {
+ showXmlDocumentation = value;
+ OnPropertyChanged("ShowXmlDocumentation");
+ }
+ }
+ }
+
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
diff --git a/ILSpy/CSharpLanguage.cs b/ILSpy/CSharpLanguage.cs
index 8e6948e14..50afed4ea 100644
--- a/ILSpy/CSharpLanguage.cs
+++ b/ILSpy/CSharpLanguage.cs
@@ -32,6 +32,7 @@ using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Ast;
using ICSharpCode.Decompiler.Ast.Transforms;
using ICSharpCode.ILSpy.Baml;
+using ICSharpCode.ILSpy.XmlDoc;
using ICSharpCode.NRefactory.CSharp;
using Mono.Cecil;
@@ -89,8 +90,7 @@ namespace ICSharpCode.ILSpy
WriteCommentLine(output, TypeToString(method.DeclaringType, includeNamespace: true));
AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: method.DeclaringType, isSingleMember: true);
codeDomBuilder.AddMethod(method);
- codeDomBuilder.RunTransformations(transformAbortCondition);
- codeDomBuilder.GenerateCode(output);
+ RunTransformsAndGenerateCode(codeDomBuilder, output, options);
}
public override void DecompileProperty(PropertyDefinition property, ITextOutput output, DecompilationOptions options)
@@ -98,8 +98,7 @@ namespace ICSharpCode.ILSpy
WriteCommentLine(output, TypeToString(property.DeclaringType, includeNamespace: true));
AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: property.DeclaringType, isSingleMember: true);
codeDomBuilder.AddProperty(property);
- codeDomBuilder.RunTransformations(transformAbortCondition);
- codeDomBuilder.GenerateCode(output);
+ RunTransformsAndGenerateCode(codeDomBuilder, output, options);
}
public override void DecompileField(FieldDefinition field, ITextOutput output, DecompilationOptions options)
@@ -107,8 +106,7 @@ namespace ICSharpCode.ILSpy
WriteCommentLine(output, TypeToString(field.DeclaringType, includeNamespace: true));
AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: field.DeclaringType, isSingleMember: true);
codeDomBuilder.AddField(field);
- codeDomBuilder.RunTransformations(transformAbortCondition);
- codeDomBuilder.GenerateCode(output);
+ RunTransformsAndGenerateCode(codeDomBuilder, output, options);
}
public override void DecompileEvent(EventDefinition ev, ITextOutput output, DecompilationOptions options)
@@ -116,16 +114,22 @@ namespace ICSharpCode.ILSpy
WriteCommentLine(output, TypeToString(ev.DeclaringType, includeNamespace: true));
AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: ev.DeclaringType, isSingleMember: true);
codeDomBuilder.AddEvent(ev);
- codeDomBuilder.RunTransformations(transformAbortCondition);
- codeDomBuilder.GenerateCode(output);
+ RunTransformsAndGenerateCode(codeDomBuilder, output, options);
}
public override void DecompileType(TypeDefinition type, ITextOutput output, DecompilationOptions options)
{
AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: type);
codeDomBuilder.AddType(type);
- codeDomBuilder.RunTransformations(transformAbortCondition);
- codeDomBuilder.GenerateCode(output);
+ RunTransformsAndGenerateCode(codeDomBuilder, output, options);
+ }
+
+ void RunTransformsAndGenerateCode(AstBuilder astBuilder, ITextOutput output, DecompilationOptions options)
+ {
+ astBuilder.RunTransformations(transformAbortCondition);
+ if (options.DecompilerSettings.ShowXmlDocumentation)
+ AddXmlDocTransform.Run(astBuilder.CompilationUnit);
+ astBuilder.GenerateCode(output);
}
public override void DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options)
diff --git a/ILSpy/DecompilerSettingsPanel.xaml b/ILSpy/DecompilerSettingsPanel.xaml
index d9f0e938e..ae1a96404 100644
--- a/ILSpy/DecompilerSettingsPanel.xaml
+++ b/ILSpy/DecompilerSettingsPanel.xaml
@@ -7,5 +7,6 @@
Decompile enumerators (yield return)
Decompile query expressions
Use variable names from debug symbols, if available
+ Show XML documentation in decompiled code
\ No newline at end of file
diff --git a/ILSpy/DecompilerSettingsPanel.xaml.cs b/ILSpy/DecompilerSettingsPanel.xaml.cs
index a9b04086b..d868afa66 100644
--- a/ILSpy/DecompilerSettingsPanel.xaml.cs
+++ b/ILSpy/DecompilerSettingsPanel.xaml.cs
@@ -62,6 +62,7 @@ namespace ICSharpCode.ILSpy
s.YieldReturn = (bool?)e.Attribute("yieldReturn") ?? s.YieldReturn;
s.QueryExpressions = (bool?)e.Attribute("queryExpressions") ?? s.QueryExpressions;
s.UseDebugSymbols = (bool?)e.Attribute("useDebugSymbols") ?? s.UseDebugSymbols;
+ s.ShowXmlDocumentation = (bool?)e.Attribute("xmlDoc") ?? s.ShowXmlDocumentation;
return s;
}
@@ -73,6 +74,7 @@ namespace ICSharpCode.ILSpy
section.SetAttributeValue("yieldReturn", s.YieldReturn);
section.SetAttributeValue("queryExpressions", s.QueryExpressions);
section.SetAttributeValue("useDebugSymbols", s.UseDebugSymbols);
+ section.SetAttributeValue("xmlDoc", s.ShowXmlDocumentation);
XElement existingElement = root.Element("DecompilerSettings");
if (existingElement != null)
diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj
index 836318799..e14465833 100644
--- a/ILSpy/ILSpy.csproj
+++ b/ILSpy/ILSpy.csproj
@@ -156,6 +156,7 @@
+
diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs
index f655e3f8a..1241037b1 100644
--- a/ILSpy/TextView/DecompilerTextView.cs
+++ b/ILSpy/TextView/DecompilerTextView.cs
@@ -138,17 +138,17 @@ namespace ICSharpCode.ILSpy.TextView
} else if (mr is MethodReference) {
mr = ((MethodReference)mr).Resolve() ?? mr;
}
+ XmlDocRenderer renderer = new XmlDocRenderer();
+ renderer.AppendText(MainWindow.Instance.CurrentLanguage.GetTooltip(mr));
XmlDocumentationProvider docProvider = XmlDocLoader.LoadDocumentation(mr.Module);
if (docProvider != null) {
- XmlDocRenderer renderer = new XmlDocRenderer();
- renderer.AppendText(MainWindow.Instance.CurrentLanguage.GetTooltip(mr));
string documentation = docProvider.GetDocumentation(XmlDocKeyProvider.GetKey(mr));
if (documentation != null) {
renderer.AppendText(Environment.NewLine);
renderer.AddXmlDocumentation(documentation);
}
- return renderer.CreateTextBlock();
}
+ return renderer.CreateTextBlock();
}
return null;
}
diff --git a/ILSpy/XmlDoc/AddXmlDocTransform.cs b/ILSpy/XmlDoc/AddXmlDocTransform.cs
new file mode 100644
index 000000000..dd0fb3311
--- /dev/null
+++ b/ILSpy/XmlDoc/AddXmlDocTransform.cs
@@ -0,0 +1,80 @@
+// 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.IO;
+using ICSharpCode.NRefactory.CSharp;
+using Mono.Cecil;
+
+namespace ICSharpCode.ILSpy.XmlDoc
+{
+ ///
+ /// Adds XML documentation for member definitions.
+ ///
+ static class AddXmlDocTransform
+ {
+ public static void Run(AstNode node)
+ {
+ if (node is AttributedNode) {
+ MemberReference mr = node.Annotation();
+ if (mr != null && mr.Module != null) {
+ var xmldoc = XmlDocLoader.LoadDocumentation(mr.Module);
+ if (xmldoc != null) {
+ string doc = xmldoc.GetDocumentation(XmlDocKeyProvider.GetKey(mr));
+ if (doc != null) {
+ InsertXmlDocumentation(node, new StringReader(doc));
+ }
+ }
+ }
+ if (!(node is TypeDeclaration))
+ return; // don't recurse into attributed nodes, except for type definitions
+ }
+ foreach (AstNode child in node.Children)
+ Run(child);
+ }
+
+ static void InsertXmlDocumentation(AstNode node, StringReader r)
+ {
+ // Find the first non-empty line:
+ string firstLine;
+ do {
+ firstLine = r.ReadLine();
+ if (firstLine == null)
+ return;
+ } while (string.IsNullOrWhiteSpace(firstLine));
+ string indentation = firstLine.Substring(0, firstLine.Length - firstLine.TrimStart().Length);
+ string line = firstLine;
+ int skippedWhitespaceLines = 0;
+ // Copy all lines from input to output, except for empty lines at the end.
+ while (line != null) {
+ if (string.IsNullOrWhiteSpace(line)) {
+ skippedWhitespaceLines++;
+ } else {
+ while (skippedWhitespaceLines > 0) {
+ node.Parent.InsertChildBefore(node, new Comment(string.Empty, CommentType.Documentation), AstNode.Roles.Comment);
+ skippedWhitespaceLines--;
+ }
+ if (line.StartsWith(indentation, StringComparison.Ordinal))
+ line = line.Substring(indentation.Length);
+ node.Parent.InsertChildBefore(node, new Comment(" " + line, CommentType.Documentation), AstNode.Roles.Comment);
+ }
+ line = r.ReadLine();
+ }
+ }
+ }
+}