diff --git a/ILSpy/Commands/PdbGenerator.cs b/ILSpy/Commands/PdbGenerator.cs
index 3bdec887b..2a75b5967 100644
--- a/ILSpy/Commands/PdbGenerator.cs
+++ b/ILSpy/Commands/PdbGenerator.cs
@@ -99,8 +99,15 @@ namespace ILSpy.Commands
if (string.IsNullOrEmpty(folder))
return;
+ // GeneratingPortablePDB is a "...for {0}..." format string; fill the placeholder with the
+ // single assembly's name, or a count when several were selected, so the tab title isn't a
+ // literal "{0}".
+ string title = supported.Count == 1
+ ? string.Format(Resources.GeneratingPortablePDB, supported.Keys.First().ShortName)
+ : string.Format(Resources.GeneratingPortablePDB, supported.Count + " assemblies");
+
// Run in a dedicated frozen tab so browsing the tree while PDBs generate can't cancel it.
- await dockWorkspace.RunInNewTabAsync(Resources.GeneratingPortablePDB, token => Task.Run(() => {
+ await dockWorkspace.RunInNewTabAsync(title, token => Task.Run(() => {
var output = new AvaloniaEditTextOutput { Title = "Generate Portable PDB" };
var totalWatch = Stopwatch.StartNew();
foreach (var (assembly, file) in supported)
@@ -129,6 +136,7 @@ namespace ILSpy.Commands
{
output.Write(string.Format(Resources.GenerationFailedForAssembly, assembly.FileName, ex.Message));
output.WriteLine();
+ output.WriteExceptionDetails(ex);
}
}
totalWatch.Stop();
diff --git a/ILSpy/SmartTextOutputExtensions.cs b/ILSpy/SmartTextOutputExtensions.cs
index 8bce49d7e..50ef8f96e 100644
--- a/ILSpy/SmartTextOutputExtensions.cs
+++ b/ILSpy/SmartTextOutputExtensions.cs
@@ -25,6 +25,7 @@ using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Media;
+using ICSharpCode.Decompiler;
using ICSharpCode.ILSpy.Properties;
using ILSpy.TextView;
@@ -34,6 +35,34 @@ namespace ILSpy
{
public static class SmartTextOutputExtensions
{
+ ///
+ /// Writes the full exception (type, message, stack trace, inner exceptions) inside a
+ /// default-collapsed "Exception details" fold. Callers write their own header line first; this
+ /// keeps a failure report scannable while leaving the trace one click away when a failure is not
+ /// self-explanatory.
+ ///
+ public static void WriteExceptionDetails(this ITextOutput output, Exception ex)
+ {
+ ArgumentNullException.ThrowIfNull(output);
+ ArgumentNullException.ThrowIfNull(ex);
+ // Blank line between the caller's header and the fold so the collapsed "Exception details"
+ // marker is visually separated from the message above it.
+ output.WriteLine();
+ output.MarkFoldStart("Exception details", true);
+ // The fold span is counted in WriteLine() calls, not in the '\n' characters embedded in a
+ // single Write(). Emit the trace one line per WriteLine() so the fold genuinely spans
+ // multiple lines; otherwise it collapses to a single line and is dropped as noise.
+ var lines = ex.ToString().Split('\n');
+ for (int i = 0; i < lines.Length; i++)
+ {
+ if (i > 0)
+ output.WriteLine();
+ output.Write(lines[i].TrimEnd('\r'));
+ }
+ output.MarkFoldEnd();
+ output.WriteLine();
+ }
+
///
/// Appends the standard "Open Explorer" result button that opens in
/// the OS file manager, followed by a blank line. Shared tail of the export / save / PDB outputs.
diff --git a/ILSpy/TreeNodes/AssemblyTreeNode.cs b/ILSpy/TreeNodes/AssemblyTreeNode.cs
index 666d9fb2b..d617cfb93 100644
--- a/ILSpy/TreeNodes/AssemblyTreeNode.cs
+++ b/ILSpy/TreeNodes/AssemblyTreeNode.cs
@@ -468,9 +468,7 @@ namespace ILSpy.TreeNodes
{
language.WriteCommentLine(output, message);
output.WriteLine();
- output.MarkFoldStart("Exception details", true);
- output.Write(ex.ToString());
- output.MarkFoldEnd();
+ output.WriteExceptionDetails(ex);
}
try