diff --git a/ILSpy.Tests/Languages/CSharpILMixedLanguageTests.cs b/ILSpy.Tests/Languages/CSharpILMixedLanguageTests.cs
new file mode 100644
index 000000000..fcae5e7a1
--- /dev/null
+++ b/ILSpy.Tests/Languages/CSharpILMixedLanguageTests.cs
@@ -0,0 +1,74 @@
+// 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.Linq;
+using System.Threading.Tasks;
+
+using Avalonia.Headless.NUnit;
+
+using AwesomeAssertions;
+
+using ILSpy;
+using ILSpy.AppEnv;
+using ILSpy.Languages;
+using ILSpy.TextView;
+using ILSpy.TreeNodes;
+
+using NUnit.Framework;
+
+namespace ICSharpCode.ILSpy.Tests.Languages;
+
+///
+/// The "IL with C#" language disassembles IL with the decompiled C# source interleaved as gray
+/// comments. (Regression: the language was missing entirely from the Avalonia port.)
+///
+[TestFixture]
+public class CSharpILMixedLanguageTests
+{
+ [AvaloniaTest]
+ public async Task ILWithCSharp_Is_Registered_And_Interleaves_CSharp_Into_The_IL()
+ {
+ var (_, vm) = await TestHarness.BootAsync(3);
+ var languages = AppComposition.Current.GetExport().Languages;
+
+ var mixed = languages.FirstOrDefault(l => l.Name == "IL with C#");
+ Assert.That(mixed, Is.Not.Null, "the 'IL with C#' language must be registered");
+ var plainIL = languages.First(l => l.Name == "IL");
+
+ var asm = vm.AssemblyTreeModel.FindNode("System.Linq");
+ Assert.That(asm, Is.Not.Null);
+ var typeSystem = asm!.LoadedAssembly.GetTypeSystemOrNull();
+ Assert.That(typeSystem, Is.Not.Null);
+ var type = typeSystem!.MainModule.TypeDefinitions.First(t => t.FullName == "System.Linq.Enumerable");
+ var method = type.Methods.First(m => m.HasBody);
+
+ string Decompile(Language language)
+ {
+ var output = new AvaloniaEditTextOutput();
+ language.DecompileMethod(method, output, new DecompilationOptions());
+ return output.GetText();
+ }
+
+ var mixedText = Decompile(mixed!);
+ var ilText = Decompile(plainIL);
+
+ mixedText.Should().Contain("//", "the decompiled C# is interleaved as comments");
+ mixedText.Length.Should().BeGreaterThan(ilText.Length,
+ "the interleaved C# source makes the mixed output longer than plain IL");
+ }
+}
diff --git a/ILSpy/Languages/CSharpILMixedLanguage.cs b/ILSpy/Languages/CSharpILMixedLanguage.cs
new file mode 100644
index 000000000..ff5944723
--- /dev/null
+++ b/ILSpy/Languages/CSharpILMixedLanguage.cs
@@ -0,0 +1,201 @@
+// Copyright (c) 2018 Siegfried Pammer
+//
+// 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.Collections.Generic;
+using System.Composition;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Reflection.Metadata;
+
+using Avalonia.Media;
+
+using AvaloniaEdit.Highlighting;
+
+using ICSharpCode.Decompiler;
+using ICSharpCode.Decompiler.CSharp;
+using ICSharpCode.Decompiler.CSharp.OutputVisitor;
+using ICSharpCode.Decompiler.CSharp.Syntax;
+using ICSharpCode.Decompiler.Disassembler;
+using ICSharpCode.Decompiler.Metadata;
+using ICSharpCode.Decompiler.TypeSystem;
+using ICSharpCode.Decompiler.Util;
+using ICSharpCode.ILSpyX;
+using ICSharpCode.ILSpyX.Extensions;
+
+using ILSpy.TextView;
+
+namespace ILSpy.Languages
+{
+ using SequencePoint = ICSharpCode.Decompiler.DebugInfo.SequencePoint;
+
+ ///
+ /// Disassembles IL with the decompiled C# source interleaved above each instruction (mapped via
+ /// sequence points) as gray comments.
+ ///
+ [Export(typeof(Language))]
+ [Shared]
+ [method: ImportingConstructor]
+ class CSharpILMixedLanguage(SettingsService settingsService) : ILLanguage
+ {
+ public override string Name => "IL with C#";
+
+ protected override ReflectionDisassembler CreateDisassembler(ITextOutput output, DecompilationOptions options)
+ {
+ var displaySettings = settingsService.DisplaySettings;
+ return new ReflectionDisassembler(output,
+ new MixedMethodBodyDisassembler(output, options) {
+ DetectControlStructure = detectControlStructure,
+ ShowSequencePoints = options.DecompilerSettings.ShowDebugInfo
+ },
+ options.CancellationToken) {
+ ShowMetadataTokens = displaySettings.ShowMetadataTokens,
+ ShowMetadataTokensInBase10 = displaySettings.ShowMetadataTokensInBase10,
+ ShowRawRVAOffsetAndBytes = displaySettings.ShowRawOffsetsAndBytesBeforeInstruction,
+ ExpandMemberDefinitions = options.DecompilerSettings.ExpandMemberDefinitions,
+ DecodeCustomAttributeBlobs = displaySettings.DecodeCustomAttributeBlobs
+ };
+ }
+
+ static CSharpDecompiler CreateDecompiler(MetadataFile module, DecompilationOptions options)
+ {
+ CSharpDecompiler decompiler = new CSharpDecompiler(module, module.GetAssemblyResolver(), options.DecompilerSettings);
+ decompiler.CancellationToken = options.CancellationToken;
+ return decompiler;
+ }
+
+ static void WriteCode(TextWriter output, DecompilerSettings settings, SyntaxTree syntaxTree, IDecompilerTypeSystem typeSystem)
+ {
+ syntaxTree.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true });
+ TokenWriter tokenWriter = new TextWriterTokenWriter(output) { IndentationString = settings.CSharpFormattingOptions.IndentationString };
+ tokenWriter = TokenWriter.WrapInWriterThatSetsLocationsInAST(tokenWriter);
+ syntaxTree.AcceptVisitor(new CSharpOutputVisitor(tokenWriter, settings.CSharpFormattingOptions));
+ }
+
+ class MixedMethodBodyDisassembler : MethodBodyDisassembler
+ {
+ readonly DecompilationOptions options;
+ // list sorted by IL offset; non-null only for the duration of a Disassemble pass
+ IList? sequencePoints;
+ // lines of raw c# source code; non-null only for the duration of a Disassemble pass
+ string[]? codeLines;
+
+ public MixedMethodBodyDisassembler(ITextOutput output, DecompilationOptions options)
+ : base(output, options.CancellationToken)
+ {
+ this.options = options;
+ }
+
+ public override void Disassemble(MetadataFile module, MethodDefinitionHandle handle)
+ {
+ try
+ {
+ var csharpOutput = new StringWriter();
+ CSharpDecompiler decompiler = CreateDecompiler(module, options);
+ var st = decompiler.Decompile(handle);
+ WriteCode(csharpOutput, options.DecompilerSettings, st, decompiler.TypeSystem);
+ var mapping = decompiler.CreateSequencePoints(st).FirstOrDefault(kvp => (kvp.Key.MoveNextMethod ?? kvp.Key.Method)?.MetadataToken == handle);
+ this.sequencePoints = mapping.Value ?? (IList)EmptyList.Instance;
+ this.codeLines = csharpOutput.ToString().Split(new[] { Environment.NewLine }, StringSplitOptions.None);
+ base.Disassemble(module, handle);
+ }
+ finally
+ {
+ this.sequencePoints = null;
+ this.codeLines = null;
+ }
+ }
+
+ protected override void WriteInstruction(ITextOutput output, MetadataFile metadata, MethodDefinitionHandle methodHandle, ref BlobReader blob, int methodRva)
+ {
+ if (sequencePoints is null || codeLines is null)
+ {
+ base.WriteInstruction(output, metadata, methodHandle, ref blob, methodRva);
+ return;
+ }
+ int index = sequencePoints.BinarySearch(blob.Offset, seq => seq.Offset);
+ if (index >= 0)
+ {
+ var info = sequencePoints[index];
+ var highlightingOutput = output as ISmartTextOutput;
+ if (!info.IsHidden)
+ {
+ for (int line = info.StartLine; line <= info.EndLine; line++)
+ {
+ if (highlightingOutput != null)
+ {
+ string text = codeLines[line - 1];
+ int startColumn = 1;
+ int endColumn = text.Length + 1;
+ if (line == info.StartLine)
+ startColumn = info.StartColumn;
+ if (line == info.EndLine)
+ endColumn = info.EndColumn;
+ WriteHighlightedCommentLine(highlightingOutput, text, startColumn - 1, endColumn - 1, info.StartLine == info.EndLine);
+ }
+ else
+ WriteCommentLine(output, codeLines[line - 1]);
+ }
+ }
+ else
+ {
+ output.Write("// ");
+ highlightingOutput?.BeginSpan(gray);
+ output.WriteLine("(no C# code)");
+ highlightingOutput?.EndSpan();
+ }
+ }
+ base.WriteInstruction(output, metadata, methodHandle, ref blob, methodRva);
+ }
+
+ HighlightingColor gray = new HighlightingColor { Foreground = new SimpleHighlightingBrush(Colors.DarkGray) };
+
+ void WriteHighlightedCommentLine(ISmartTextOutput output, string text, int startColumn, int endColumn, bool isSingleLine)
+ {
+ if (startColumn > text.Length)
+ {
+ Debug.Fail("startColumn is invalid");
+ startColumn = text.Length;
+ }
+ if (endColumn > text.Length)
+ {
+ Debug.Fail("endColumn is invalid");
+ endColumn = text.Length;
+ }
+ output.Write("// ");
+ output.BeginSpan(gray);
+ if (isSingleLine)
+ output.Write(text.Substring(0, startColumn).TrimStart());
+ else
+ output.Write(text.Substring(0, startColumn));
+ output.EndSpan();
+ output.Write(text.Substring(startColumn, endColumn - startColumn));
+ output.BeginSpan(gray);
+ output.Write(text.Substring(endColumn));
+ output.EndSpan();
+ output.WriteLine();
+ }
+
+ void WriteCommentLine(ITextOutput output, string text)
+ {
+ output.WriteLine("// " + text);
+ }
+ }
+ }
+}