diff --git a/ILSpy/App.axaml b/ILSpy/App.axaml index c3a391f26..59fd1f3a5 100644 --- a/ILSpy/App.axaml +++ b/ILSpy/App.axaml @@ -5,6 +5,7 @@ xmlns:tree="using:ILSpy.AssemblyTree" xmlns:search="using:ILSpy.Search" xmlns:analyzers="using:ILSpy.Analyzers" + xmlns:textView="using:ILSpy.TextView" xmlns:dockTheme="using:Dock.Avalonia.Themes.Simple" xmlns:controls="using:ILSpy.Controls" RequestedThemeVariant="Default"> @@ -35,12 +36,16 @@ + + + + diff --git a/ILSpy/Languages/CSharpLanguage.cs b/ILSpy/Languages/CSharpLanguage.cs index c877c05de..b555590ec 100644 --- a/ILSpy/Languages/CSharpLanguage.cs +++ b/ILSpy/Languages/CSharpLanguage.cs @@ -17,12 +17,21 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Collections.Generic; using System.Composition; +using System.Diagnostics; +using System.Linq; +using System.Reflection.Metadata; using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.CSharp.OutputVisitor; +using ICSharpCode.Decompiler.CSharp.Syntax; +using ICSharpCode.Decompiler.CSharp.Transforms; +using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.ILSpyX; using ConversionFlags = ICSharpCode.Decompiler.Output.ConversionFlags; @@ -60,5 +69,77 @@ namespace ILSpy.Languages | ConversionFlags.ShowParameterModifiers; return ambience.ConvertSymbol(entity); } + + public override void WriteCommentLine(ITextOutput output, string comment) => output.WriteLine("// " + comment); + + public override void DecompileType(ITypeDefinition type, ITextOutput output, DecompilationOptions options) + { + Debug.Assert(type.ParentModule?.MetadataFile != null); + DecompileEntities(type.ParentModule.MetadataFile, new[] { type.MetadataToken }, output, options); + } + + public override void DecompileMethod(IMethod method, ITextOutput output, DecompilationOptions options) + { + Debug.Assert(method.ParentModule?.MetadataFile != null); + DecompileEntities(method.ParentModule.MetadataFile, new[] { method.MetadataToken }, output, options); + } + + public override void DecompileField(IField field, ITextOutput output, DecompilationOptions options) + { + Debug.Assert(field.ParentModule?.MetadataFile != null); + DecompileEntities(field.ParentModule.MetadataFile, new[] { field.MetadataToken }, output, options); + } + + public override void DecompileProperty(IProperty property, ITextOutput output, DecompilationOptions options) + { + Debug.Assert(property.ParentModule?.MetadataFile != null); + DecompileEntities(property.ParentModule.MetadataFile, new[] { property.MetadataToken }, output, options); + } + + public override void DecompileEvent(IEvent ev, ITextOutput output, DecompilationOptions options) + { + Debug.Assert(ev.ParentModule?.MetadataFile != null); + DecompileEntities(ev.ParentModule.MetadataFile, new[] { ev.MetadataToken }, output, options); + } + + public override void DecompileNamespace(string nameSpace, IEnumerable types, ITextOutput output, DecompilationOptions options) + { + var typesByModule = types.GroupBy(t => { + Debug.Assert(t.ParentModule?.MetadataFile != null); + return t.ParentModule.MetadataFile; + }); + bool first = true; + foreach (var group in typesByModule) + { + if (!first) + output.WriteLine(); + first = false; + DecompileEntities(group.Key, group.Select(t => t.MetadataToken), output, options); + } + } + + void DecompileEntities(MetadataFile module, IEnumerable handles, ITextOutput output, DecompilationOptions options) + { + if (module == null) + { + WriteCommentLine(output, "(metadata file unavailable)"); + return; + } + var resolver = module.GetAssemblyResolver(options.DecompilerSettings.AutoLoadAssemblyReferences); + var decompiler = new CSharpDecompiler(module, resolver, options.DecompilerSettings) { + CancellationToken = options.CancellationToken, + DebugInfoProvider = module.GetDebugInfoOrNull(), + }; + SyntaxTree syntaxTree = decompiler.Decompile(handles); + WriteCode(output, options.DecompilerSettings, syntaxTree, decompiler.TypeSystem); + } + + static void WriteCode(ITextOutput output, DecompilerSettings settings, SyntaxTree syntaxTree, IDecompilerTypeSystem typeSystem) + { + syntaxTree.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true }); + output.IndentationString = settings.CSharpFormattingOptions.IndentationString; + TokenWriter tokenWriter = new TextTokenWriter(output, settings, typeSystem); + syntaxTree.AcceptVisitor(new CSharpOutputVisitor(tokenWriter, settings.CSharpFormattingOptions)); + } } } diff --git a/ILSpy/Languages/Language.cs b/ILSpy/Languages/Language.cs index d323bd0b6..784bc82d6 100644 --- a/ILSpy/Languages/Language.cs +++ b/ILSpy/Languages/Language.cs @@ -17,6 +17,8 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Collections.Generic; +using System.Diagnostics; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.IL; @@ -26,7 +28,7 @@ using ICSharpCode.Decompiler.TypeSystem; namespace ILSpy.Languages { /// - /// Output language for tree-node labels and (eventually) decompiled views. + /// Output language for tree-node labels and decompiled views. /// Subclasses override formatting for their target syntax. Implementations must be /// thread-safe. /// @@ -63,6 +65,52 @@ namespace ILSpy.Languages return ambience.ConvertSymbol(entity); } + /// + /// Writes as a single-line comment in this language's syntax. + /// + public virtual void WriteCommentLine(ITextOutput output, string comment) + { + output.WriteLine("// " + comment); + } + + // Default Decompile* implementations write a stub comment so we always produce *something* + // for symbols whose language doesn't have a meaningful decompilation. Real languages + // (CSharpLanguage, eventually ILLanguage) override these. + + public virtual void DecompileType(ITypeDefinition type, ITextOutput output, DecompilationOptions options) + { + WriteCommentLine(output, TypeToString(type)); + } + + public virtual void DecompileMethod(IMethod method, ITextOutput output, DecompilationOptions options) + { + Debug.Assert(method.DeclaringTypeDefinition != null); + WriteCommentLine(output, TypeToString(method.DeclaringTypeDefinition) + "." + method.Name); + } + + public virtual void DecompileField(IField field, ITextOutput output, DecompilationOptions options) + { + Debug.Assert(field.DeclaringTypeDefinition != null); + WriteCommentLine(output, TypeToString(field.DeclaringTypeDefinition) + "." + field.Name); + } + + public virtual void DecompileProperty(IProperty property, ITextOutput output, DecompilationOptions options) + { + Debug.Assert(property.DeclaringTypeDefinition != null); + WriteCommentLine(output, TypeToString(property.DeclaringTypeDefinition) + "." + property.Name); + } + + public virtual void DecompileEvent(IEvent ev, ITextOutput output, DecompilationOptions options) + { + Debug.Assert(ev.DeclaringTypeDefinition != null); + WriteCommentLine(output, TypeToString(ev.DeclaringTypeDefinition) + "." + ev.Name); + } + + public virtual void DecompileNamespace(string nameSpace, IEnumerable types, ITextOutput output, DecompilationOptions options) + { + WriteCommentLine(output, nameSpace); + } + public override string ToString() => Name; } } diff --git a/ILSpy/TextView/Asm-Mode.xshd b/ILSpy/TextView/Asm-Mode.xshd new file mode 100644 index 000000000..70dc222c4 --- /dev/null +++ b/ILSpy/TextView/Asm-Mode.xshd @@ -0,0 +1,1215 @@ + + + + + + + + + + + + + + + aaa + aad + aam + aas + adc + add + and + call + cbw + cdqe + clc + cld + cli + cmc + cmp + cmps + cmpsb + cmpsw + cwd + daa + das + dec + div + esc + hlt + idiv + imul + in + inc + int + into + iret + ja + jae + jb + jbe + jc + jcxz + je + jg + jge + jl + jle + jmp + jna + jnae + jnb + jnbe + jnc + jne + jng + jnge + jnl + jnle + jno + jnp + jns + jnz + jo + jp + jpe + jpo + js + jz + lahf + lds + lea + les + lods + lodsb + lodsw + loop + loope + loopew + loopne + loopnew + loopnz + loopnzw + loopw + loopz + loopzw + mov + movabs + movs + movsb + movsw + mul + neg + nop + not + or + out + pop + popf + push + pushf + rcl + rcr + ret + retf + retn + rol + ror + sahf + sal + sar + sbb + scas + scasb + scasw + shl + shr + stc + std + sti + stos + stosb + stosw + sub + test + wait + xchg + xlat + xlatb + xor + bound + enter + ins + insb + insw + leave + outs + outsb + outsw + popa + pusha + pushw + arpl + lar + lsl + sgdt + sidt + sldt + smsw + str + verr + verw + clts + lgdt + lidt + lldt + lmsw + ltr + bsf + bsr + bt + btc + btr + bts + cdq + cmpsd + cwde + insd + iretd + iretdf + iretf + jecxz + lfs + lgs + lodsd + loopd + looped + loopned + loopnzd + loopzd + lss + movsd + movsx + movsxd + movzx + outsd + popad + popfd + pushad + pushd + pushfd + scasd + seta + setae + setb + setbe + setc + sete + setg + setge + setl + setle + setna + setnae + setnb + setnbe + setnc + setne + setng + setnge + setnl + setnle + setno + setnp + setns + setnz + seto + setp + setpe + setpo + sets + setz + shld + shrd + stosd + bswap + cmpxchg + invd + invlpg + wbinvd + xadd + lock + rep + repe + repne + repnz + repz + cflush + cpuid + emms + femms + cmovo + cmovno + cmovb + cmovc + cmovnae + cmovae + cmovnb + cmovnc + cmove + cmovz + cmovne + cmovnz + cmovbe + cmovna + cmova + cmovnbe + cmovs + cmovns + cmovp + cmovpe + cmovnp + cmovpo + cmovl + cmovnge + cmovge + cmovnl + cmovle + cmovng + cmovg + cmovnle + cmpxchg486 + cmpxchg8b + loadall + loadall286 + ibts + icebp + int1 + int3 + int01 + int03 + iretw + popaw + popfw + pushaw + pushfw + rdmsr + rdpmc + rdshr + rdtsc + rsdc + rsldt + rsm + rsts + salc + smi + smint + smintold + svdc + svldt + svts + syscall + sysenter + sysexit + sysret + ud0 + ud1 + ud2 + umov + xbts + wrmsr + wrshr + + + f2xm1 + fabs + fadd + faddp + fbld + fbstp + fchs + fclex + fcom + fcomp + fcompp + fdecstp + fdisi + fdiv + fdivp + fdivr + fdivrp + feni + ffree + fiadd + ficom + ficomp + fidiv + fidivr + fild + fimul + fincstp + finit + fist + fistp + fisub + fisubr + fld + fld1 + fldcw + fldenv + fldenvw + fldl2e + fldl2t + fldlg2 + fldln2 + fldpi + fldz + fmul + fmulp + fnclex + fndisi + fneni + fninit + fnop + fnsave + fnsavew + fnstcw + fnstenv + fnstenvw + fnstsw + fpatan + fprem + fptan + frndint + frstor + frstorw + fsave + fsavew + fscale + fsqrt + fst + fstcw + fstenv + fstenvw + fstp + fstsw + fsub + fsubp + fsubr + fsubrp + ftst + fwait + fxam + fxch + fxtract + fyl2x + fyl2xp1 + fsetpm + fcos + fldenvd + fnsaved + fnstenvd + fprem1 + frstord + fsaved + fsin + fsincos + fstenvd + fucom + fucomp + fucompp + fcomi + fcomip + ffreep + fcmovb + fcmove + fcmovbe + fcmovu + fcmovnb + fcmovne + fcmovnbe + fcmovnu + + + ah + al + ax + bh + bl + bp + bx + ch + cl + cr0 + cr2 + cr3 + cr4 + cs + cx + dh + di + dl + dr0 + dr1 + dr2 + dr3 + dr6 + dr7 + ds + dx + eax + ebp + ebx + ecx + edi + edx + es + esi + esp + fs + gs + rax + rbx + rcx + rdx + rdi + rsi + rbp + rsp + r8 + r9 + r10 + r11 + r12 + r13 + r14 + r15 + r8d + r9d + r10d + r11d + r12d + r13d + r14d + r15d + r8w + r9w + r10w + r11w + r12w + r13w + r14w + r15w + r8b + r9b + r10b + r11b + r12b + r13b + r14b + r15b + si + sp + ss + st + tr3 + tr4 + tr5 + tr6 + tr7 + st0 + st1 + st2 + st3 + st4 + st5 + st6 + st7 + mm0 + mm1 + mm2 + mm3 + mm4 + mm5 + mm6 + mm7 + xmm0 + xmm1 + xmm2 + xmm3 + xmm4 + xmm5 + xmm6 + xmm7 + xmm8 + xmm9 + xmm10 + xmm11 + xmm12 + xmm13 + xmm14 + xmm15 + + + .186 + .286 + .286c + .286p + .287 + .386 + .386c + .386p + .387 + .486 + .486p + .8086 + .8087 + .alpha + .break + .code + .const + .continue + .cref + .data + .data? + .dosseg + .else + .elseif + .endif + .endw + .err + .err1 + .err2 + .errb + .errdef + .errdif + .errdifi + .erre + .erridn + .erridni + .errnb + .errndef + .errnz + .exit + .fardata + .fardata? + .if + .lall + .lfcond + .list + .listall + .listif + .listmacro + .listmacroall + .model + .no87 + .nocref + .nolist + .nolistif + .nolistmacro + .radix + .repeat + .sall + .seq + .sfcond + .stack + .startup + .tfcond + .type + .until + .untilcxz + .while + .xall + .xcref + .xlist + alias + align + assume + catstr + comm + comment + db + dd + df + dosseg + dq + dt + dup + dw + echo + else + elseif + elseif1 + elseif2 + elseifb + elseifdef + elseifdif + elseifdifi + elseife + elseifidn + elseifidni + elseifnb + elseifndef + end + endif + endm + endp + ends + eq + equ + even + exitm + extern + externdef + extrn + for + forc + ge + goto + group + gt + high + highword + if + if1 + if2 + ifb + ifdef + ifdif + ifdifi + ife + ifidn + ifidni + ifnb + ifndef + include + includelib + instr + invoke + irp + irpc + label + le + length + lengthof + local + low + lowword + lroffset + lt + macro + mask + mod + .msfloat + name + ne + offset + opattr + option + org + %out + page + popcontext + proc + proto + ptr + public + purge + pushcontext + record + repeat + rept + seg + segment + short + size + sizeof + sizestr + struc + struct + substr + subtitle + subttl + textequ + this + title + type + typedef + union + while + width + resb + resw + resd + resq + rest + incbin + times + %define + %idefine + %xdefine + %xidefine + %undef + %assign + %iassign + %strlen + %substr + %macro + %imacro + %endmacro + %rotate + %if + %elif + %else + %endif + %ifdef + %ifndef + %elifdef + %elifndef + %ifmacro + %ifnmacro + %elifmacro + %elifnmacro + %ifctk + %ifnctk + %elifctk + %elifnctk + %ifidn + %ifnidn + %elifidn + %elifnidn + %ifidni + %ifnidni + %elifidni + %elifnidni + %ifid + %ifnid + %elifid + %elifnid + %ifstr + %ifnstr + %elifstr + %elifnstr + %ifnum + %ifnnum + %elifnum + %elifnnum + %error + %rep + %endrep + %exitrep + %include + %push + %pop + %repl + endstruc + istruc + at + iend + alignb + %arg + %stacksize + %local + %line + bits + use16 + use32 + section + absolute + global + common + cpu + import + export + + + $ + ? + @b + @f + addr + basic + byte + c + carry? + dword + far + far16 + fortran + fword + near + near16 + overflow? + parity? + pascal + qword + real4 + real8 + real10 + sbyte + sdword + sign? + stdcall + sword + syscall + tbyte + vararg + word + zero? + flat + near32 + far32 + abs + all + assumes + at + casemap + common + compact + cpu + dotname + emulator + epilogue + error + export + expr16 + expr32 + farstack + forceframe + huge + language + large + listing + ljmp + loadds + m510 + medium + memory + nearstack + nodotname + noemulator + nokeyword + noljmp + nom510 + none + nonunique + nooldmacros + nooldstructs + noreadonly + noscoped + nosignextend + nothing + notpublic + oldmacros + oldstructs + os_dos + para + private + prologue + radix + readonly + req + scoped + setif2 + smallstack + tiny + use16 + use32 + uses + a16 + a32 + o16 + o32 + nosplit + $$ + seq + wrt + small + .text + .data + .bss + %0 + %1 + %2 + %3 + %4 + %5 + %6 + %7 + %8 + %9 + + + addpd + addps + addsd + addss + andpd + andps + andnpd + andnps + cmpeqpd + cmpltpd + cmplepd + cmpunordpd + cmpnepd + cmpnltpd + cmpnlepd + cmpordpd + cmpeqps + cmpltps + cmpleps + cmpunordps + cmpneps + cmpnltps + cmpnleps + cmpordps + cmpeqsd + cmpltsd + cmplesd + cmpunordsd + cmpnesd + cmpnltsd + cmpnlesd + cmpordsd + cmpeqss + cmpltss + cmpless + cmpunordss + cmpness + cmpnltss + cmpnless + cmpordss + comisd + comiss + cvtdq2pd + cvtdq2ps + cvtpd2dq + cvtpd2pi + cvtpd2ps + cvtpi2pd + cvtpi2ps + cvtps2dq + cvtps2pd + cvtps2pi + cvtss2sd + cvtss2si + cvtsd2si + cvtsd2ss + cvtsi2sd + cvtsi2ss + cvttpd2dq + cvttpd2pi + cvttps2dq + cvttps2pi + cvttsd2si + cvttss2si + divpd + divps + divsd + divss + fxrstor + fxsave + ldmxscr + lfence + mfence + maskmovdqu + maskmovdq + maxpd + maxps + paxsd + maxss + minpd + minps + minsd + minss + movapd + movaps + movdq2q + movdqa + movdqu + movhlps + movhpd + movhps + movd + movq + movlhps + movlpd + movlps + movmskpd + movmskps + movntdq + movnti + movntpd + movntps + movntq + movq2dq + movsd + movss + movupd + movups + mulpd + mulps + mulsd + mulss + orpd + orps + packssdw + packsswb + packuswb + paddb + paddsb + paddw + paddsw + paddd + paddsiw + paddq + paddusb + paddusw + pand + pandn + pause + paveb + pavgb + pavgw + pavgusb + pdistib + pextrw + pcmpeqb + pcmpeqw + pcmpeqd + pcmpgtb + pcmpgtw + pcmpgtd + pf2id + pf2iw + pfacc + pfadd + pfcmpeq + pfcmpge + pfcmpgt + pfmax + pfmin + pfmul + pmachriw + pmaddwd + pmagw + pmaxsw + pmaxub + pminsw + pminub + pmovmskb + pmulhrwc + pmulhriw + pmulhrwa + pmulhuw + pmulhw + pmullw + pmuludq + pmvzb + pmvnzb + pmvlzb + pmvgezb + pfnacc + pfpnacc + por + prefetch + prefetchw + prefetchnta + prefetcht0 + prefetcht1 + prefetcht2 + pfrcp + pfrcpit1 + pfrcpit2 + pfrsqit1 + pfrsqrt + pfsub + pfsubr + pi2fd + pinsrw + psadbw + pshufd + pshufhw + pshuflw + pshufw + psllw + pslld + psllq + pslldq + psraw + psrad + psrlw + psrld + psrlq + psrldq + psubb + psubw + psubd + psubq + psubsb + psubsw + psubusb + psubusw + psubsiw + pswapd + punpckhbw + punpckhwd + punpckhdq + punpckhqdq + punpcklbw + punpcklwd + punpckldq + punpcklqdq + pxor + rcpps + rcpss + rsqrtps + rsqrtss + sfence + shufpd + shufps + sqrtpd + sqrtps + sqrtsd + sqrtss + stmxcsr + subpd + subps + subsd + subss + ucomisd + ucomiss + unpckhpd + unpckhps + unpcklpd + unpcklps + xorpd + xorps + + + ; + + + ^ \s* [0-9A-F]+ \s+ [0-9A-F]+ + + + \b(0[xXhH])?[0-9a-fA-F_`]+[h]? # hex number + | + ( \b\d+(\.[0-9]+)? #number with optional floating point + | \.[0-9]+ #or just starting with floating point + ) + ([eE][+-]?[0-9]+)? # optional exponent + + + + + TODO + FIXME + + + HACK + UNDONE + + + \ No newline at end of file diff --git a/ILSpy/TextView/AvaloniaEditTextOutput.cs b/ILSpy/TextView/AvaloniaEditTextOutput.cs new file mode 100644 index 000000000..8b0f34881 --- /dev/null +++ b/ILSpy/TextView/AvaloniaEditTextOutput.cs @@ -0,0 +1,106 @@ +// Copyright (c) 2026 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.Reflection.Metadata; +using System.Text; + +using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.Disassembler; +using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.TypeSystem; + +namespace ILSpy.TextView +{ + /// + /// Phase-1 for the Avalonia text view: just accumulates text into a + /// with the indentation contract the decompiler expects. Reference + /// markers, fold ranges, and inline UI elements are intentionally dropped — those will land + /// when we add hyperlinks and folding support. + /// + sealed class AvaloniaEditTextOutput : ITextOutput + { + readonly StringBuilder builder = new(); + int indent; + bool needsIndent; + + public string IndentationString { get; set; } = "\t"; + + public int TextLength => builder.Length; + + public string GetText() => builder.ToString(); + + public void Indent() => indent++; + + public void Unindent() + { + if (indent > 0) + indent--; + } + + void WriteIndentIfNeeded() + { + if (!needsIndent) + return; + needsIndent = false; + for (int i = 0; i < indent; i++) + builder.Append(IndentationString); + } + + public void Write(char ch) + { + WriteIndentIfNeeded(); + builder.Append(ch); + } + + public void Write(string text) + { + WriteIndentIfNeeded(); + builder.Append(text); + } + + public void WriteLine() + { + builder.Append('\n'); + needsIndent = true; + } + + public void WriteReference(OpCodeInfo opCode, bool omitSuffix = false) + { + Write(omitSuffix ? opCode.Name.TrimEnd('.') : opCode.Name); + } + + public void WriteReference(MetadataFile metadata, Handle handle, string text, string protocol = "decompile", bool isDefinition = false) + => Write(text); + + public void WriteReference(IType type, string text, bool isDefinition = false) => Write(text); + + public void WriteReference(IMember member, string text, bool isDefinition = false) => Write(text); + + public void WriteLocalReference(string text, object reference, bool isDefinition = false) => Write(text); + + public void MarkFoldStart(string collapsedText = "...", bool defaultCollapsed = false, bool isDefinition = false) + { + // Folding support arrives in a later phase. + } + + public void MarkFoldEnd() + { + // Folding support arrives in a later phase. + } + } +} diff --git a/ILSpy/TextView/CSharp-Mode.xshd b/ILSpy/TextView/CSharp-Mode.xshd new file mode 100644 index 000000000..e3f502de9 --- /dev/null +++ b/ILSpy/TextView/CSharp-Mode.xshd @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TODO + FIXME + + + HACK + UNDONE + + + + + + + \# + + + + (define|undef|if|elif|else|endif|line)\b + + + + // + + + + + + (region|endregion|error|warning|pragma)\b + + + + + + + ///(?!/) + + + + + + + + // + + + + /\* + \*/ + + + + " + " + + + + + + + + ' + ' + + + + + + + + @" + " + + + + + + + + \$" + " + + + + + + + + + + + + \b0[xX][0-9a-fA-F]+ # hex number + | + ( \b\d+(\.[0-9]+)? #number with optional floating point + | \.[0-9]+ #or just starting with floating point + ) + ([eE][+-]?[0-9]+)? # optional exponent + + + diff --git a/ILSpy/TextView/DecompilerTabPageModel.cs b/ILSpy/TextView/DecompilerTabPageModel.cs new file mode 100644 index 000000000..60037a059 --- /dev/null +++ b/ILSpy/TextView/DecompilerTabPageModel.cs @@ -0,0 +1,125 @@ +// Copyright (c) 2026 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.Threading; +using System.Threading.Tasks; + +using Avalonia.Threading; + +using CommunityToolkit.Mvvm.ComponentModel; + +using ICSharpCode.Decompiler; + +using ILSpy.Languages; +using ILSpy.TreeNodes; +using ILSpy.ViewModels; + +namespace ILSpy.TextView +{ + /// + /// A document tab that hosts decompiled output for a single tree node. Re-decompiles when + /// changes; previous in-flight decompilations are cancelled so a + /// rapid tree-selection sweep doesn't pile up background work. + /// + public sealed partial class DecompilerTabPageModel : TabPageModel + { + CancellationTokenSource? activeCts; + + [ObservableProperty] + private string text = string.Empty; + + /// + /// File extension driving syntax highlighting (e.g. ".cs"). Updated alongside . + /// + [ObservableProperty] + private string syntaxExtension = ".cs"; + + ILSpyTreeNode? currentNode; + + public ILSpyTreeNode? CurrentNode { + get => currentNode; + set { + if (currentNode == value) + return; + currentNode = value; + _ = DecompileAsync(); + } + } + + public DecompilerTabPageModel() + { + Title = "Empty"; + } + + public Language Language { get; set; } = null!; + + async Task DecompileAsync() + { + activeCts?.Cancel(); + var cts = activeCts = new CancellationTokenSource(); + var node = currentNode; + var language = Language; + if (node == null || language == null) + { + Text = string.Empty; + return; + } + + Title = node.Text?.ToString() ?? "(unnamed)"; + Text = "Decompiling…"; + SyntaxExtension = language.FileExtension; + + try + { + var (output, _) = await Task.Run(() => { + var output = new AvaloniaEditTextOutput(); + var options = new DecompilationOptions { CancellationToken = cts.Token }; + try + { + node.Decompile(language, output, options); + } + catch (OperationCanceledException) + { + // expected on cancel — just return whatever we got + } + catch (Exception ex) + { + output.WriteLine(); + output.WriteLine("/* Decompilation failed:"); + output.WriteLine(ex.ToString()); + output.WriteLine("*/"); + } + return (output, cts.Token); + }, cts.Token).ConfigureAwait(true); + + if (cts.Token.IsCancellationRequested) + return; + + var rendered = output.GetText(); + await Dispatcher.UIThread.InvokeAsync(() => { + Text = rendered; + }); + } + catch (OperationCanceledException) + { + // stale request — drop silently + } + } + } +} diff --git a/ILSpy/TextView/DecompilerTextView.axaml b/ILSpy/TextView/DecompilerTextView.axaml new file mode 100644 index 000000000..987836bd6 --- /dev/null +++ b/ILSpy/TextView/DecompilerTextView.axaml @@ -0,0 +1,16 @@ + + + + diff --git a/ILSpy/TextView/DecompilerTextView.axaml.cs b/ILSpy/TextView/DecompilerTextView.axaml.cs new file mode 100644 index 000000000..369d8c68a --- /dev/null +++ b/ILSpy/TextView/DecompilerTextView.axaml.cs @@ -0,0 +1,59 @@ +// Copyright (c) 2026 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.ComponentModel; + +using Avalonia.Controls; + +namespace ILSpy.TextView +{ + public partial class DecompilerTextView : UserControl + { + public DecompilerTextView() + { + InitializeComponent(); + } + + protected override void OnDataContextChanged(System.EventArgs e) + { + base.OnDataContextChanged(e); + if (DataContext is DecompilerTabPageModel model) + { + model.PropertyChanged += OnModelPropertyChanged; + ApplyDocument(model); + } + } + + void OnModelPropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (sender is DecompilerTabPageModel model + && (e.PropertyName == nameof(DecompilerTabPageModel.Text) + || e.PropertyName == nameof(DecompilerTabPageModel.SyntaxExtension))) + { + ApplyDocument(model); + } + } + + void ApplyDocument(DecompilerTabPageModel model) + { + Editor.SyntaxHighlighting = HighlightingService.GetByExtension(model.SyntaxExtension); + Editor.Document.Text = model.Text; + Editor.ScrollToHome(); + } + } +} diff --git a/ILSpy/TextView/HighlightingService.cs b/ILSpy/TextView/HighlightingService.cs new file mode 100644 index 000000000..cfcf4a3a4 --- /dev/null +++ b/ILSpy/TextView/HighlightingService.cs @@ -0,0 +1,79 @@ +// Copyright (c) 2026 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 System.Xml; + +using AvaloniaEdit.Highlighting; +using AvaloniaEdit.Highlighting.Xshd; + +namespace ILSpy.TextView +{ + /// + /// Loads our XSHD highlighting definitions (CSharp / IL / Asm / XML) from embedded resources + /// once and registers them with AvaloniaEdit's . Lookup by + /// file extension (".cs" / ".il" / ".asm" / ".xml") matches what + /// returns. + /// + static class HighlightingService + { + static bool registered; + static readonly object gate = new(); + + // Embedded resource names follow the csproj RootNamespace ("ICSharpCode.ILSpy"), + // not the file's C# namespace ("ILSpy"). + const string ResourcePrefix = "ICSharpCode.ILSpy.TextView."; + + public static void EnsureRegistered() + { + if (registered) + return; + lock (gate) + { + if (registered) + return; + Register("C#", new[] { ".cs" }, "CSharp-Mode.xshd"); + Register("IL", new[] { ".il" }, "ILAsm-Mode.xshd"); + Register("Asm", new[] { ".asm" }, "Asm-Mode.xshd"); + Register("XML", new[] { ".xml", ".xaml" }, "XML-Mode.xshd"); + registered = true; + } + } + + public static IHighlightingDefinition? GetByExtension(string fileExtension) + { + EnsureRegistered(); + return HighlightingManager.Instance.GetDefinitionByExtension(fileExtension); + } + + static void Register(string name, string[] extensions, string resourceName) + { + HighlightingManager.Instance.RegisterHighlighting(name, extensions, () => Load(resourceName)); + } + + static IHighlightingDefinition Load(string resourceName) + { + using var stream = typeof(HighlightingService).Assembly + .GetManifestResourceStream(ResourcePrefix + resourceName) + ?? throw new InvalidOperationException($"Highlighting resource not found: {resourceName}"); + using var reader = XmlReader.Create(stream); + return HighlightingLoader.Load(reader, HighlightingManager.Instance); + } + } +} diff --git a/ILSpy/TextView/ILAsm-Mode.xshd b/ILSpy/TextView/ILAsm-Mode.xshd new file mode 100644 index 000000000..e376f9410 --- /dev/null +++ b/ILSpy/TextView/ILAsm-Mode.xshd @@ -0,0 +1,540 @@ + + + + + + + + + + + + nop + break + ldarg.0 + ldarg.1 + ldarg.2 + ldarg.3 + ldloc.0 + ldloc.1 + ldloc.2 + ldloc.3 + stloc.0 + stloc.1 + stloc.2 + stloc.3 + ldarg.s + ldarga.s + starg.s + ldloc.s + ldloca.s + stloc.s + ldnull + ldc.i4.m1 + ldc.i4.0 + ldc.i4.1 + ldc.i4.2 + ldc.i4.3 + ldc.i4.4 + ldc.i4.5 + ldc.i4.6 + ldc.i4.7 + ldc.i4.8 + ldc.i4.s + ldc.i4 + ldc.i8 + ldc.r4 + ldc.r8 + dup + pop + jmp + call + calli + ret + br.s + brfalse.s + brtrue.s + beq.s + bge.s + bgt.s + ble.s + blt.s + bne.un.s + bge.un.s + bgt.un.s + ble.un.s + blt.un.s + br + brfalse + brtrue + beq + bge + bgt + ble + blt + bne.un + bge.un + bgt.un + ble.un + blt.un + switch + ldind.i1 + ldind.u1 + ldind.i2 + ldind.u2 + ldind.i4 + ldind.u4 + ldind.i8 + ldind.i + ldind.r4 + ldind.r8 + ldind.ref + stind.ref + stind.i1 + stind.i2 + stind.i4 + stind.i8 + stind.r4 + stind.r8 + add + sub + mul + div + div.un + rem + rem.un + and + or + xor + shl + shr + shr.un + neg + not + conv.i1 + conv.i2 + conv.i4 + conv.i8 + conv.r4 + conv.r8 + conv.u4 + conv.u8 + callvirt + cpobj + ldobj + ldstr + newobj + castclass + isinst + conv.r.un + unbox + throw + ldfld + ldflda + stfld + ldsfld + ldsflda + stsfld + stobj + conv.ovf.i1.un + conv.ovf.i2.un + conv.ovf.i4.un + conv.ovf.i8.un + conv.ovf.u1.un + conv.ovf.u2.un + conv.ovf.u4.un + conv.ovf.u8.un + conv.ovf.i.un + conv.ovf.u.un + box + newarr + ldlen + ldelema + ldelem + ldelem.i1 + ldelem.u1 + ldelem.i2 + ldelem.u2 + ldelem.i4 + ldelem.u4 + ldelem.i8 + ldelem.i + ldelem.r4 + ldelem.r8 + ldelem.ref + stelem + stelem.i + stelem.i1 + stelem.i2 + stelem.i4 + stelem.i8 + stelem.r4 + stelem.r8 + stelem.ref + conv.ovf.i1 + conv.ovf.u1 + conv.ovf.i2 + conv.ovf.u2 + conv.ovf.i4 + conv.ovf.u4 + conv.ovf.i8 + conv.ovf.u8 + refanyval + ckfinite + mkrefany + ldtoken + conv.u2 + conv.u1 + conv.i + conv.ovf.i + conv.ovf.u + add.ovf + add.ovf.un + mul.ovf + mul.ovf.un + sub.ovf + sub.ovf.un + endfinally + leave + leave.s + stind.i + conv.u + prefix7 + prefix6 + prefix5 + prefix4 + prefix3 + prefix2 + prefix1 + prefixref + arglist + ceq + cgt + cgt.un + clt + clt.un + ldftn + ldvirtftn + ldarg + ldarga + starg + ldloc + ldloca + stloc + localloc + endfilter + unaligned. + volatile. + tail. + initobj + cpblk + initblk + rethrow + sizeof + refanytype + illegal + endmac + brnull + brnull.s + brzero + brzero.s + brinst + brinst.s + ldind.u8 + ldelem.u8 + ldc.i4.M1 + endfault + + + void + bool + char + wchar + int + int8 + int16 + int32 + int64 + uint8 + uint16 + uint32 + uint64 + float + float32 + float64 + refany + typedref + object + string + native + unsigned + value + valuetype + class + const + vararg + default + stdcall + thiscall + fastcall + unmanaged + not_in_gc_heap + beforefieldinit + instance + filter + catch + static + public + private + synchronized + interface + extends + implements + handler + finally + fault + to + abstract + auto + sequential + explicit + wrapper + ansi + unicode + autochar + import + enum + virtual + notremotable + special + il + cil + optil + managed + preservesig + runtime + method + field + bytearray + final + sealed + specialname + family + assembly + famandassem + famorassem + privatescope + nested + hidebysig + newslot + rtspecialname + pinvokeimpl + unmanagedexp + reqsecobj + .ctor + .cctor + initonly + literal + notserialized + forwardref + internalcall + noinlining + aggressiveinlining + nomangle + lasterr + winapi + cdecl + stdcall + thiscall + fastcall + as + pinned + modreq + modopt + serializable + at + tls + true + false + strict + type + + + .class + .namespace + .method + .field + .emitbyte + .try + .maxstack + .locals + .entrypoint + .zeroinit + .pdirect + .data + .event + .addon + .removeon + .fire + .other + protected + .property + .set + .get + default + .import + .permission + .permissionset + .line + .language + .interfaceimpl + #line + + + request + demand + assert + deny + permitonly + linkcheck + inheritcheck + reqmin + reqopt + reqrefuse + prejitgrant + prejitdeny + noncasdemand + noncaslinkdemand + noncasinheritance + + + + .custom + + init + + .size + .pack + + .file + nometadata + .hash + .assembly + implicitcom + noappdomain + noprocess + nomachine + .publickey + .publickeytoken + algorithm + .ver + .locale + extern + .export + .manifestres + .mresource + .localized + + + .module + marshal + custom + sysstring + fixed + variant + currency + syschar + decimal + date + bstr + tbstr + lpstr + lpwstr + lptstr + objectref + iunknown + idispatch + struct + safearray + byvalstr + lpvoid + any + array + lpstruct + + + .vtfixup + fromunmanaged + callmostderived + .vtentry + + + in + out + opt + lcid + retval + .param + + + .override + with + + + null + error + hresult + carray + userdefined + record + filetime + blob + stream + storage + streamed_object + stored_object + blob_object + cf + clsid + vector + + + nullref + + + .subsystem + .corflags + .stackreserve + alignment + .imagebase + + + // + + + /\* + \*/ + + + " + " + + + ' + ' + + + ^ \s* \w+ : + + + + + TODO + FIXME + + + HACK + UNDONE + + + \ No newline at end of file diff --git a/ILSpy/TextView/XML-Mode.xshd b/ILSpy/TextView/XML-Mode.xshd new file mode 100644 index 000000000..8f0bdef76 --- /dev/null +++ b/ILSpy/TextView/XML-Mode.xshd @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + <!-- + --> + + + <!\[CDATA\[ + ]]> + + + <!DOCTYPE + > + + + <\? + \?> + + + < + > + + + + " + "|(?=<) + + + ' + '|(?=<) + + [\d\w_\-\.]+(?=(\s*=)) + = + + + + + + + + & + [\w\d\#]+ + ; + + + + & + [\w\d\#]* + #missing ; + + + \ No newline at end of file diff --git a/ILSpy/TreeNodes/EventTreeNode.cs b/ILSpy/TreeNodes/EventTreeNode.cs index 3bd9386c7..d6913fc89 100644 --- a/ILSpy/TreeNodes/EventTreeNode.cs +++ b/ILSpy/TreeNodes/EventTreeNode.cs @@ -18,9 +18,12 @@ using System; +using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.TypeSystem; +using ILSpy.Languages; + namespace ILSpy.TreeNodes { sealed class EventTreeNode : ILSpyTreeNode @@ -37,6 +40,9 @@ namespace ILSpy.TreeNodes Images.Images.GetOverlay(EventDefinition.Accessibility), EventDefinition.IsStatic); public override bool ShowExpander => false; + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + => language.DecompileEvent(EventDefinition, output, options); + public override string ToString() => "Event " + EventDefinition.Name; } } diff --git a/ILSpy/TreeNodes/FieldTreeNode.cs b/ILSpy/TreeNodes/FieldTreeNode.cs index 2ac6bfe2b..02b248f85 100644 --- a/ILSpy/TreeNodes/FieldTreeNode.cs +++ b/ILSpy/TreeNodes/FieldTreeNode.cs @@ -18,9 +18,12 @@ using System; +using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.TypeSystem; +using ILSpy.Languages; + namespace ILSpy.TreeNodes { sealed class FieldTreeNode : ILSpyTreeNode @@ -37,6 +40,9 @@ namespace ILSpy.TreeNodes Images.Images.GetOverlay(FieldDefinition.Accessibility), FieldDefinition.IsStatic); public override bool ShowExpander => false; + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + => language.DecompileField(FieldDefinition, output, options); + public override string ToString() => "Field " + FieldDefinition.Name; } } diff --git a/ILSpy/TreeNodes/ILSpyTreeNode.cs b/ILSpy/TreeNodes/ILSpyTreeNode.cs index b7f473424..9862e3dbe 100644 --- a/ILSpy/TreeNodes/ILSpyTreeNode.cs +++ b/ILSpy/TreeNodes/ILSpyTreeNode.cs @@ -16,6 +16,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using ICSharpCode.Decompiler; using ICSharpCode.ILSpyX.TreeView; using ILSpy.AppEnv; @@ -35,5 +36,15 @@ namespace ILSpy.TreeNodes => cachedLanguageService ??= AppComposition.Current.GetExport(); public Language Language => LanguageService.CurrentLanguage; + + /// + /// Renders this node's decompiled representation to using + /// . Default writes a stub comment so any node we forgot to + /// override still produces *something*. + /// + public virtual void Decompile(Language language, ITextOutput output, DecompilationOptions options) + { + language.WriteCommentLine(output, Text?.ToString() ?? GetType().Name); + } } } diff --git a/ILSpy/TreeNodes/MethodTreeNode.cs b/ILSpy/TreeNodes/MethodTreeNode.cs index 8c198d056..6bec54357 100644 --- a/ILSpy/TreeNodes/MethodTreeNode.cs +++ b/ILSpy/TreeNodes/MethodTreeNode.cs @@ -18,9 +18,12 @@ using System; +using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.TypeSystem; +using ILSpy.Languages; + namespace ILSpy.TreeNodes { sealed class MethodTreeNode : ILSpyTreeNode @@ -48,6 +51,9 @@ namespace ILSpy.TreeNodes public override bool ShowExpander => false; + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + => language.DecompileMethod(MethodDefinition, output, options); + // Stable identity for SessionSettings.ActiveTreeViewPath; matches the WPF host's format. public override string ToString() => "Method " + new ICSharpCode.Decompiler.IL.ILAmbience { diff --git a/ILSpy/TreeNodes/NamespaceTreeNode.cs b/ILSpy/TreeNodes/NamespaceTreeNode.cs index 170d96045..55b2b8bde 100644 --- a/ILSpy/TreeNodes/NamespaceTreeNode.cs +++ b/ILSpy/TreeNodes/NamespaceTreeNode.cs @@ -19,7 +19,12 @@ using System; using System.Linq; +using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.ILSpyX; + +using ILSpy.Languages; namespace ILSpy.TreeNodes { @@ -55,5 +60,18 @@ namespace ILSpy.TreeNodes foreach (var t in types) Children.Add(new TypeTreeNode(t, module)); } + + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + { + var typeSystem = module.GetTypeSystemOrNull(); + if (typeSystem == null) + { + language.WriteCommentLine(output, "(type system unavailable)"); + return; + } + var types = typeSystem.MainModule.TypeDefinitions + .Where(t => t.Namespace == name && t.DeclaringTypeDefinition == null); + language.DecompileNamespace(name, types, output, options); + } } } diff --git a/ILSpy/TreeNodes/PropertyTreeNode.cs b/ILSpy/TreeNodes/PropertyTreeNode.cs index 7f2cc6759..516a8e4ae 100644 --- a/ILSpy/TreeNodes/PropertyTreeNode.cs +++ b/ILSpy/TreeNodes/PropertyTreeNode.cs @@ -18,9 +18,12 @@ using System; +using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.TypeSystem; +using ILSpy.Languages; + namespace ILSpy.TreeNodes { sealed class PropertyTreeNode : ILSpyTreeNode @@ -37,6 +40,9 @@ namespace ILSpy.TreeNodes Images.Images.GetOverlay(PropertyDefinition.Accessibility), PropertyDefinition.IsStatic); public override bool ShowExpander => false; + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + => language.DecompileProperty(PropertyDefinition, output, options); + public override string ToString() => "Property " + new ICSharpCode.Decompiler.IL.ILAmbience { ConversionFlags = ConversionFlags.ShowTypeParameterList diff --git a/ILSpy/TreeNodes/TypeTreeNode.cs b/ILSpy/TreeNodes/TypeTreeNode.cs index 6a4b4a616..54c40ee44 100644 --- a/ILSpy/TreeNodes/TypeTreeNode.cs +++ b/ILSpy/TreeNodes/TypeTreeNode.cs @@ -26,6 +26,8 @@ using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpyX; +using ILSpy.Languages; + namespace ILSpy.TreeNodes { sealed class TypeTreeNode : ILSpyTreeNode @@ -71,6 +73,15 @@ namespace ILSpy.TreeNodes public override bool CanExpandRecursively => true; + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + { + var typeDef = ResolveTypeDefinition(); + if (typeDef != null) + language.DecompileType(typeDef, output, options); + else + language.WriteCommentLine(output, "(could not resolve type)"); + } + // Stable identity for SessionSettings.ActiveTreeViewPath. ReflectionName is // language-independent. public override string ToString()