diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml
index 79bc706..c16194a 100644
--- a/.github/workflows/CI.yml
+++ b/.github/workflows/CI.yml
@@ -19,14 +19,6 @@ jobs:
distribution: 'corretto'
java-version: '21'
cache: 'gradle'
- - uses: actions/cache@v4
- with:
- path: |
- build/gradle-jvm
- ~/.nuget/packages
- ~/.gradle/caches
- ~/.gradle/wrapper
- key: ${{ runner.os }}-Build-${{ hashFiles('gradlew.bat', 'src/dotnet/*/*.csproj', './*.props', 'gradle-wrapper.properties') }}
- run: ./gradlew :buildPlugin --no-daemon
- run: ./gradlew :buildResharperPlugin --no-daemon
- uses: actions/upload-artifact@v4
@@ -46,13 +38,4 @@ jobs:
distribution: 'corretto'
java-version: '17'
cache: 'gradle'
- - uses: actions/cache@v4
- with:
- path: |
- build/gradle-jvm
- packages
- ~/.nuget/packages
- ~/.gradle/caches
- ~/.gradle/wrapper
- key: ${{ runner.os }}-Test-${{ hashFiles('gradlew.bat', 'src/dotnet/*/*.csproj', './*.props', 'gradle-wrapper.properties') }}
- run: ./gradlew :testDotNet --no-daemon
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 51f246f..7a6fe60 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
# Changelog
+## 2024.2
+ * Adds support for [Parent=""] attributes
+ * Makes the New Mod template modular, allowing you to select the components you want included in your mod
+ * Bundled in Zetrith's Remodder code into this plugin
+ * Update the Rimworld template for 1.6
+ * Automatically detect and use the user-installed Harmony version for debugging, if available
+
## 2024.1.7
* Solved some incompatibilities for ReSharper
diff --git a/Directory.Build.props b/Directory.Build.props
index 20c468f..066f197 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -39,6 +39,7 @@
+ allruntime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/README.md b/README.md
index 2a5257b..49d2279 100644
--- a/README.md
+++ b/README.md
@@ -19,12 +19,14 @@ into the definitions on which the XML sits.
* Autocomplete DefNames when using `DefDatabase.GetNamed()`
* Autocomplete DefNames when creating fields in `[DefOf]` classes
* Autocomplete certain values for properties with fixed options (Such as Altitude Layer, boolean and directions)
+ * Autocompletion for `Parent=""` attributes
* Use `Ctrl+Click` to go references
* When using them on DefTypes, just to the C# class for that Def
* When using them on XML Properties, jump to the C# definition for that property
* When using them on DefName references, jump to the XML definition for that DefName
* When using them on certain XML values, jump to the C# definition for that value
* When using them on `[DefOf]` fields, or `DefDatabase.GetNamed()` calls, jump to the XML definition for that DefName
+ * When using them on `Parent=""` attributes, jump to the XML definition for that parent
* Read the values in `Class=""` attributes to fetch the correct class to autocomplete from, such as in comps
* Support for Custom Def Classes (Such as ``)
* A Rimworld Run Configuration
@@ -38,6 +40,7 @@ into the definitions on which the XML sits.
* The ability to perform Find Usages on XML Defs
* A "Generate" menu option to generate properties for a given XML Def
* Includes a Rimworld Dictionary so that Rimworld terms don't get flagged as not real words by Rider
+ * Transpilation Explorer, curtesy of [Zetrith](https://github.com/Zetrith/Remodder)
## Quick Architecture For Developers
@@ -75,6 +78,10 @@ our own custom context provider which grabs the solution from the XML File and a
The other is the [`RimworldXmlReference`](./src/dotnet/ReSharperPlugin.RimworldDev/References/RimworldXmlReference.cs)
which is just a class that we have to define to hold our reference from the XML to the C#.
+## Acknowledgements
+The feature of exploring transpiled code is taken from Zetrith's open source plugin, [Remodder](https://github.com/Zetrith/Remodder).
+The feature would not have happened without them.
+
## FAQ
**VS Code Plugin When?**
diff --git a/build.gradle.kts b/build.gradle.kts
index 407f416..263698c 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,5 +1,7 @@
+import com.jetbrains.plugin.structure.base.utils.isFile
import groovy.ant.FileNameFinder
import org.apache.tools.ant.taskdefs.condition.Os
+import org.jetbrains.intellij.platform.gradle.Constants
import org.jetbrains.intellij.platform.gradle.IntelliJPlatformType
import java.io.ByteArrayOutputStream
@@ -7,7 +9,6 @@ plugins {
id("java")
alias(libs.plugins.kotlinJvm)
id("org.jetbrains.intellij.platform") version "2.5.0" // https://github.com/JetBrains/gradle-intellij-plugin/releases
-// id("com.jetbrains.rdgen") version libs.versions.rdGen // https://www.myget.org/feed/rd-snapshots/package/maven/com.jetbrains.rd/rd-gen
id("me.filippov.gradle.jvm.wrapper") version "0.14.0"
}
@@ -196,6 +197,14 @@ tasks.prepareSandbox {
val dllFiles = listOf(
"$outputFolder/${DotnetPluginId}.dll",
"$outputFolder/${DotnetPluginId}.pdb",
+
+ // Not 100% sure why, but we manually need to include these dependencies for Remodder to work
+ "$outputFolder/0Harmony.dll",
+ "$outputFolder/AsmResolver.dll",
+ "$outputFolder/AsmResolver.DotNet.dll",
+ "$outputFolder/AsmResolver.PE.dll",
+ "$outputFolder/AsmResolver.PE.File.dll",
+ "$outputFolder/ICSharpCode.Decompiler.dll"
)
dllFiles.forEach({ f ->
@@ -243,4 +252,21 @@ tasks.patchPluginXml {
pluginVersion.set(PluginVersion)
changeNotes.set("
\r\n$changelogText\r\n
");
untilBuild.set(provider { null })
+}
+
+val riderModel: Configuration by configurations.creating {
+ isCanBeConsumed = true
+ isCanBeResolved = false
+}
+
+artifacts {
+ add(riderModel.name, provider {
+ intellijPlatform.platformPath.resolve("lib/rd/rider-model.jar").also {
+ check(it.isFile) {
+ "rider-model.jar is not found at $riderModel"
+ }
+ }
+ }) {
+ builtBy(Constants.Tasks.INITIALIZE_INTELLIJ_PLATFORM_PLUGIN)
+ }
}
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index d6599b7..a32a40b 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -4,7 +4,7 @@
DotnetPluginId=ReSharperPlugin.RimworldDev
DotnetSolution=ReSharperPlugin.RimworldDev.sln
RiderPluginId=com.jetbrains.rider.plugins.rimworlddev
-PluginVersion=2024.1.7
+PluginVersion=2024.2
BuildConfiguration=Release
@@ -25,4 +25,7 @@ rdVersion=2025.1
rdKotlinVersion=2.1.0
intellijPlatformGradlePluginVersion=2.2.1
gradleJvmWrapperVersion=0.14.0
-riderBaseVersion=2025.1
\ No newline at end of file
+riderBaseVersion=2025.1
+
+# Required to download Rider artifacts from Maven (and not "binary" releases from CDN).
+org.jetbrains.intellij.platform.buildFeature.useBinaryReleases=false
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index c52f4c0..ec205ef 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,5 +1,5 @@
[versions]
-kotlin = "2.1.20" # https://plugins.jetbrains.com/docs/intellij/using-kotlin.html#kotlin-standard-library
+kotlin = "2.1.10" # https://plugins.jetbrains.com/docs/intellij/using-kotlin.html#kotlin-standard-library
rdGen = "2025.1.1" # https://github.com/JetBrains/rd/releases
[libraries]
diff --git a/protocol/build.gradle.kts b/protocol/build.gradle.kts
new file mode 100644
index 0000000..2ee7928
--- /dev/null
+++ b/protocol/build.gradle.kts
@@ -0,0 +1,52 @@
+import com.jetbrains.rd.generator.gradle.RdGenTask
+
+plugins {
+ id("org.jetbrains.kotlin.jvm")
+ id("com.jetbrains.rdgen") version libs.versions.rdGen
+}
+
+dependencies {
+ implementation(libs.kotlinStdLib)
+ implementation(libs.rdGen)
+ implementation(
+ project(
+ mapOf(
+ "path" to ":",
+ "configuration" to "riderModel"
+ )
+ )
+ )
+}
+
+val DotnetPluginId: String by rootProject
+val RiderPluginId: String by rootProject
+
+rdgen {
+ val csOutput = File(rootDir, "src/dotnet/${DotnetPluginId}")
+ val ktOutput = File(rootDir, "src/rider/main/kotlin/remodder")
+
+ verbose = true
+ packages = "model.rider"
+
+ generator {
+ language = "kotlin"
+ transform = "asis"
+ root = "com.jetbrains.rider.model.nova.ide.IdeRoot"
+ namespace = "com.jetbrains.rider.model"
+ directory = "$ktOutput"
+ }
+
+ generator {
+ language = "csharp"
+ transform = "reversed"
+ root = "com.jetbrains.rider.model.nova.ide.IdeRoot"
+ namespace = "JetBrains.Rider.Model"
+ directory = "$csOutput"
+ }
+}
+
+tasks.withType {
+ val classPath = sourceSets["main"].runtimeClasspath
+ dependsOn(classPath)
+ classpath(classPath)
+}
\ No newline at end of file
diff --git a/protocol/src/main/kotlin/model/rider/Model.kt b/protocol/src/main/kotlin/model/rider/Model.kt
new file mode 100644
index 0000000..877d28d
--- /dev/null
+++ b/protocol/src/main/kotlin/model/rider/Model.kt
@@ -0,0 +1,18 @@
+package model.rider
+
+import com.jetbrains.rd.generator.nova.Ext
+import com.jetbrains.rd.generator.nova.csharp.CSharp50Generator
+import com.jetbrains.rd.generator.nova.kotlin.Kotlin11Generator
+import com.jetbrains.rd.generator.nova.*
+import com.jetbrains.rd.generator.nova.PredefinedType.*
+import com.jetbrains.rider.model.nova.ide.SolutionModel
+
+object RemodderProtocolModel : Ext(SolutionModel.Solution) {
+ init {
+ setting(CSharp50Generator.Namespace, "ReSharperPlugin.RdProtocol")
+ setting(Kotlin11Generator.Namespace, "com.jetbrains.rider.plugins.rdprotocol")
+
+ // Remote procedure on backend
+ call("decompile", array(string), array(string)).async
+ }
+}
\ No newline at end of file
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 024b8ab..4a6834b 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -35,11 +35,11 @@ pluginManagement {
resolutionStrategy {
eachPlugin {
- when (requested.id.name) {
- // This required to correctly rd-gen plugin resolution. May be we should switch our naming to match Gradle plugin naming convention.
- "rdgen" -> {
- useModule("com.jetbrains.rd:rd-gen:${rdVersion}")
- }
+ // Gradle has to map a plugin dependency to Maven coordinates - '{groupId}:{artifactId}:{version}'. It tries
+ // to do use '{plugin.id}:{plugin.id}.gradle.plugin:version'.
+ // This doesn't work for rdgen, so we provide some help
+ if (requested.id.id == "com.jetbrains.rdgen") {
+ useModule("com.jetbrains.rd:rd-gen:${requested.version}")
}
}
}
@@ -54,4 +54,6 @@ dependencyResolutionManagement {
maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-eap")
maven("https://cache-redirector.jetbrains.com/myget.org.rd-snapshots.maven")
}
-}
\ No newline at end of file
+}
+
+include(":protocol")
\ No newline at end of file
diff --git a/src/dotnet/ReSharperPlugin.RimworldDev.Tests/ReSharperPlugin.RimworldDev.Tests.csproj b/src/dotnet/ReSharperPlugin.RimworldDev.Tests/ReSharperPlugin.RimworldDev.Tests.csproj
index 5088e5a..79c9f7b 100644
--- a/src/dotnet/ReSharperPlugin.RimworldDev.Tests/ReSharperPlugin.RimworldDev.Tests.csproj
+++ b/src/dotnet/ReSharperPlugin.RimworldDev.Tests/ReSharperPlugin.RimworldDev.Tests.csproj
@@ -1,7 +1,7 @@
- net472
+ net6.0false
diff --git a/src/dotnet/ReSharperPlugin.RimworldDev/ProjectTemplates/RimworldProjectTemplate/.template.config/template.json b/src/dotnet/ReSharperPlugin.RimworldDev/ProjectTemplates/RimworldProjectTemplate/.template.config/template.json
index 99bcb8d..3e4e45e 100644
--- a/src/dotnet/ReSharperPlugin.RimworldDev/ProjectTemplates/RimworldProjectTemplate/.template.config/template.json
+++ b/src/dotnet/ReSharperPlugin.RimworldDev/ProjectTemplates/RimworldProjectTemplate/.template.config/template.json
@@ -41,6 +41,38 @@
"isRequired": "false",
"defaultValue": ""
},
+ "PublisherPlus": {
+ "type": "parameter",
+ "datatype": "bool",
+ "displayName": "Include a PublisherPlus config",
+ "description": "If enabled, a PublisherPlus config file will be included in the mod",
+ "isRequired": "false",
+ "defaultValue": "true"
+ },
+ "CSharp": {
+ "type": "parameter",
+ "datatype": "bool",
+ "displayName": "Add a C# Project and Sources",
+ "description": "If enabled, this mod will be created with a .sln file, Sources folder and a csproj file",
+ "isRequired": "false",
+ "defaultValue": "true"
+ },
+ "XML": {
+ "type": "parameter",
+ "datatype": "bool",
+ "displayName": "Add a Defs folder with a starting XML file",
+ "description": "If enabled, this mod will be created with a Defs folder and a starting XML file",
+ "isRequired": "false",
+ "defaultValue": "true"
+ },
+ "Languages": {
+ "type": "parameter",
+ "datatype": "bool",
+ "displayName": "Add a Languages folder",
+ "description": "If enabled, this mod will be created with a Languages folder with a starting English.xml file",
+ "isRequired": "false",
+ "defaultValue": "true"
+ },
"AssemblyDir": {
"type": "derived",
"valueSource": "RimworldDLL",
@@ -64,11 +96,22 @@
{
"type": "conditional",
"configuration": {
- "if": [ "
+
+ AssemblyDir\System.dll
+ False
+
+
+ AssemblyDir\System.Core.dll
+ False
+
+
+ AssemblyDir\System.Xml.dll
+ False
+ Assembly-CSharp.dllFalse
@@ -52,7 +58,7 @@
If that reference does not exist, it will add Krafs Rimworld Ref to the project. If it does exist, Krafs won't be added
as a reference. This basically means that Krafs is treated as a fallback if Assembly-CSharp is not found -->
-
+
+ true
@@ -20,8 +23,15 @@
true
+
+
+
+
+
+
+
@@ -42,4 +52,14 @@
+
+
+
+
+
+
+ JetResourceGenerator
+
+
+
\ No newline at end of file
diff --git a/src/dotnet/ReSharperPlugin.RimworldDev/ReSharperPlugin.RimworldDev.csproj b/src/dotnet/ReSharperPlugin.RimworldDev/ReSharperPlugin.RimworldDev.csproj
index bd8468d..edf475a 100644
--- a/src/dotnet/ReSharperPlugin.RimworldDev/ReSharperPlugin.RimworldDev.csproj
+++ b/src/dotnet/ReSharperPlugin.RimworldDev/ReSharperPlugin.RimworldDev.csproj
@@ -8,7 +8,7 @@
- net472
+ net6.0True$(DefineConstants);RESHARPERfalse
@@ -42,6 +42,9 @@
+
+
+
diff --git a/src/dotnet/ReSharperPlugin.RimworldDev/References/RimworldReferenceProvider.cs b/src/dotnet/ReSharperPlugin.RimworldDev/References/RimworldReferenceProvider.cs
index f3f43bc..d24c37e 100644
--- a/src/dotnet/ReSharperPlugin.RimworldDev/References/RimworldReferenceProvider.cs
+++ b/src/dotnet/ReSharperPlugin.RimworldDev/References/RimworldReferenceProvider.cs
@@ -50,6 +50,12 @@ public ReferenceCollection GetReferences(ITreeNode element, ReferenceCollection
element.Parent.GetText().StartsWith(""))
return GetReferenceForDeclaredElement(element, oldReferences);
+ if (element.Parent != null && element.NodeType.ToString() == "STRING" &&
+ (element.Parent.GetText().StartsWith("Name") || element.Parent.GetText().StartsWith("ParentName")))
+ {
+ return GetReferenceForDeclaredElement(element, oldReferences);
+ }
+
if (element.NodeType.ToString() == "TEXT") return GetReferencesForText(element, oldReferences);
if (element is not XmlIdentifier identifier) return new ReferenceCollection();
if (element.GetSourceFile() is not { } sourceFile) return new ReferenceCollection();
@@ -129,7 +135,6 @@ private ReferenceCollection GetReferencesForText(ITreeNode element, ReferenceCol
if (classContext == null) return new ReferenceCollection();
var rimworldSymbolScope = ScopeHelper.RimworldScope;
- var allSymbolScopes = ScopeHelper.AllScopes;
if (classContext.GetType().Name == "Enum")
{
@@ -137,7 +142,6 @@ private ReferenceCollection GetReferencesForText(ITreeNode element, ReferenceCol
var col = new ReferenceCollection(new RimworldXmlReference(@class, element));
return col;
- // return new ReferenceCollection();
}
if (!ScopeHelper.ExtendsFromVerseDef(classContext.GetClrName().FullName))
@@ -169,15 +173,21 @@ private ReferenceCollection GetReferencesForText(ITreeNode element, ReferenceCol
private ReferenceCollection GetReferenceForDeclaredElement(ITreeNode element, ReferenceCollection oldReferences)
{
// We're currently in a text node inside a inside another ThingDef node. We want to get that node
- var defTypeName = element.Parent?.Parent?
+ // Alternatively, we may be in a string node inside a Name Attribute, so we need to go a step further
+ var parentTag = element.Parent?.Parent;
+ if (parentTag is not XmlTag && parentTag?.Parent is XmlTag) parentTag = parentTag.Parent;
+
+ if (parentTag is null) return new ReferenceCollection();
+
+ var defTypeName = parentTag
// And then get the TagHeader () of that node
.Children().FirstOrDefault(childElement => childElement is XmlTagHeaderNode)?
// And then get the text that provides the ID of that node (ThingDef)
.Children().FirstOrDefault(childElement => childElement is XmlIdentifier)?.GetText();
- if (defTypeName is null) new ReferenceCollection();
-
- var defName = element.GetText();
+ if (defTypeName is null) return new ReferenceCollection();
+
+ var defName = element.GetText().Trim('"');
var xmlSymbolTable = element.GetSolution().GetComponent();
diff --git a/src/dotnet/ReSharperPlugin.RimworldDev/Remodder/AssemblyHelper.cs b/src/dotnet/ReSharperPlugin.RimworldDev/Remodder/AssemblyHelper.cs
new file mode 100644
index 0000000..28d37ae
--- /dev/null
+++ b/src/dotnet/ReSharperPlugin.RimworldDev/Remodder/AssemblyHelper.cs
@@ -0,0 +1,47 @@
+using System.IO;
+using AsmResolver;
+using AsmResolver.PE;
+using AsmResolver.PE.DotNet.Builder;
+using AsmResolver.PE.DotNet.Metadata.Strings;
+using AsmResolver.PE.DotNet.Metadata.Tables;
+using AsmResolver.PE.DotNet.Metadata.Tables.Rows;
+
+namespace ReSharperPlugin.RimworldDev.Remodder;
+
+public static class AssemblyHelper
+{
+ public static byte[] RemoveReferenceAssemblyAttribute(byte[] input)
+ {
+ var peImage = PEImage.FromBytes(input);
+ var tables = peImage.DotNetDirectory.Metadata.GetStream();
+ var customAttrsTable = tables.GetTable();
+ var memberRefTable = tables.GetTable();
+ var typeRefTable = tables.GetTable();
+ var stringsStream = peImage.DotNetDirectory.Metadata.GetStream();
+
+ for (int i = customAttrsTable.Count - 1; i >= 0; i--)
+ {
+ var r = customAttrsTable[i];
+ if ((r.Type & 0b111) == 3)
+ {
+ var ctor = memberRefTable.GetByRid(r.Type >> 3);
+ if ((ctor.Parent & 0b111) == 1)
+ {
+ var type = typeRefTable.GetByRid(ctor.Parent >> 3);
+ if (stringsStream.GetStringByIndex(type.Name) == "ReferenceAssemblyAttribute")
+ {
+ customAttrsTable.Remove(r);
+ }
+ }
+ }
+ }
+
+ var fileBuilder = new ManagedPEFileBuilder(EmptyErrorListener.Instance);
+ var file = fileBuilder.CreateFile(peImage);
+
+ var stream = new MemoryStream();
+ file.Write(stream);
+
+ return stream.ToArray();
+ }
+}
\ No newline at end of file
diff --git a/src/dotnet/ReSharperPlugin.RimworldDev/Remodder/Decompiler.cs b/src/dotnet/ReSharperPlugin.RimworldDev/Remodder/Decompiler.cs
new file mode 100644
index 0000000..a17d50d
--- /dev/null
+++ b/src/dotnet/ReSharperPlugin.RimworldDev/Remodder/Decompiler.cs
@@ -0,0 +1,200 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Threading;
+using HarmonyLib;
+using ICSharpCode.Decompiler;
+using ICSharpCode.Decompiler.CSharp;
+using ICSharpCode.Decompiler.DebugInfo;
+using ICSharpCode.Decompiler.Disassembler;
+using ICSharpCode.Decompiler.Metadata;
+using ICSharpCode.Decompiler.TypeSystem;
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+using MonoMod.Utils;
+
+// Taken from my old TranspilerExplorer project
+// Badly needs a rework...
+namespace ReSharperPlugin.RimworldDev.Remodder;
+
+public static class Decompiler
+{
+ const string OrigType = "OrigType";
+ const string DummyDll = "decomp.dll";
+
+ public static AttributePatch? GetTranspiler(Assembly asm, string typeName)
+ {
+ var type = asm.GetType(typeName);
+ if (type == null)
+ return null;
+ var transpiler = new Harmony("dummy").
+ CreateClassProcessor(type).patchMethods?.
+ FirstOrDefault(p => p.type == HarmonyPatchType.Transpiler);
+ return transpiler;
+ }
+
+ public static string Decompile(MethodBase orig, MethodInfo? transpiler, string[] userAsms, IDebugInfoProvider? debugInfo)
+ {
+ using var stream = new MemoryStream();
+ WriteAssembly(stream, orig, transpiler);
+ stream.Position = 0;
+
+ using var peFile = new PEFile(DummyDll, stream);
+ using var writer = new StringWriter();
+
+ var assemblyResolver = new UniversalAssemblyResolver(
+ userAsms.FirstOrDefault(),
+ false,
+ peFile.DetectTargetFrameworkId(),
+ peFile.DetectRuntimePack()
+ );
+
+ foreach (var userAsm in userAsms.Skip(1))
+ {
+ var dir = Path.GetDirectoryName(userAsm);
+ if (!string.IsNullOrEmpty(dir) && !assemblyResolver.GetSearchDirectories().Contains(dir))
+ assemblyResolver.AddSearchDirectory(Path.GetDirectoryName(userAsm));
+ }
+
+ var settings = new DecompilerSettings
+ {
+ ThrowOnAssemblyResolveErrors = false,
+ AnonymousMethods = false,
+ UseDebugSymbols = debugInfo != null
+ };
+
+ var decompiler = new CSharpDecompiler(peFile, assemblyResolver, settings)
+ {
+ DebugInfoProvider = debugInfo,
+ };
+
+ var code = decompiler.DecompileTypeAsString(new FullTypeName(orig.DeclaringType?.Name ?? OrigType));
+
+ return code;
+ }
+
+ public static string Disasm(MethodBase orig, MethodInfo transpiler)
+ {
+ using var stream = new MemoryStream();
+ WriteAssembly(stream, orig, transpiler);
+ stream.Position = 0;
+
+ using var peFile = new PEFile(DummyDll, stream);
+ using var writer = new StringWriter();
+
+ var output = new PlainTextOutput(writer);
+ ReflectionDisassembler rd = new ReflectionDisassembler(output, CancellationToken.None);
+ rd.DetectControlStructure = false;
+ rd.DisassembleType(peFile, peFile.GetTypeDefinition(new TopLevelTypeName(orig.DeclaringType?.Name ?? OrigType)));
+
+ return writer.ToString();
+ }
+
+ static void WriteAssembly(Stream stream, MethodBase orig, MethodInfo? transpiler)
+ {
+ var patch = MethodPatcher.CreateDynamicMethod(orig, "", false);
+ var il = patch.GetILGenerator();
+ var originalVariables = MethodPatcher.DeclareOriginalLocalVariables(il, orig);
+ var copier = new MethodCopier(orig, il, originalVariables);
+ var emitter = new Emitter(il, false);
+
+ copier.AddTranspiler(transpiler ?? identityTranspiler);
+
+ var endLabels = new List