|
|
|
|
@ -19,10 +19,10 @@
@@ -19,10 +19,10 @@
|
|
|
|
|
using System; |
|
|
|
|
using System.Collections.Generic; |
|
|
|
|
using System.Composition; |
|
|
|
|
using System.Diagnostics; |
|
|
|
|
using System.IO; |
|
|
|
|
using System.Linq; |
|
|
|
|
using System.Reflection.Metadata; |
|
|
|
|
using System.Reflection.PortableExecutable; |
|
|
|
|
|
|
|
|
|
using AvaloniaEdit.Highlighting; |
|
|
|
|
|
|
|
|
|
@ -31,9 +31,12 @@ using ICSharpCode.Decompiler.CSharp;
@@ -31,9 +31,12 @@ using ICSharpCode.Decompiler.CSharp;
|
|
|
|
|
using ICSharpCode.Decompiler.CSharp.OutputVisitor; |
|
|
|
|
using ICSharpCode.Decompiler.CSharp.Syntax; |
|
|
|
|
using ICSharpCode.Decompiler.CSharp.Transforms; |
|
|
|
|
using ICSharpCode.Decompiler.IL; |
|
|
|
|
using ICSharpCode.Decompiler.Metadata; |
|
|
|
|
using ICSharpCode.Decompiler.Output; |
|
|
|
|
using ICSharpCode.Decompiler.Solution; |
|
|
|
|
using ICSharpCode.Decompiler.TypeSystem; |
|
|
|
|
using ICSharpCode.ILSpy.Properties; |
|
|
|
|
using ICSharpCode.ILSpyX; |
|
|
|
|
|
|
|
|
|
using ConversionFlags = ICSharpCode.Decompiler.Output.ConversionFlags; |
|
|
|
|
@ -77,10 +80,6 @@ namespace ILSpy.Languages
@@ -77,10 +80,6 @@ namespace ILSpy.Languages
|
|
|
|
|
|
|
|
|
|
public override RichText GetRichTextTooltip(IEntity entity) |
|
|
|
|
{ |
|
|
|
|
// Mirrors ICSharpCode.ILSpy.Languages.CSharpLanguage.GetRichTextTooltip: hand the
|
|
|
|
|
// entity to a CSharpAmbience whose token sink is wrapped in CSharpHighlightingTokenWriter
|
|
|
|
|
// so we get semantic colours alongside the text. Body and trailing-return-type bits
|
|
|
|
|
// are stripped — the tooltip is a one-line signature.
|
|
|
|
|
ArgumentNullException.ThrowIfNull(entity); |
|
|
|
|
var flags = ConversionFlags.All & ~(ConversionFlags.ShowBody | ConversionFlags.PlaceReturnTypeAfterParameterList); |
|
|
|
|
var output = new StringWriter(); |
|
|
|
|
@ -92,66 +91,302 @@ namespace ILSpy.Languages
@@ -92,66 +91,302 @@ namespace ILSpy.Languages
|
|
|
|
|
return new RichText(output.ToString(), writer.HighlightingModel); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public override void DecompileType(ITypeDefinition type, ITextOutput output, DecompilationOptions options) |
|
|
|
|
CSharpDecompiler CreateDecompiler(MetadataFile module, DecompilationOptions options) |
|
|
|
|
{ |
|
|
|
|
Debug.Assert(type.ParentModule?.MetadataFile != null); |
|
|
|
|
DecompileEntities(type.ParentModule.MetadataFile, new[] { type.MetadataToken }, output, options); |
|
|
|
|
var decompiler = new CSharpDecompiler(module, module.GetAssemblyResolver(options.DecompilerSettings.AutoLoadAssemblyReferences), options.DecompilerSettings) { |
|
|
|
|
CancellationToken = options.CancellationToken, |
|
|
|
|
DebugInfoProvider = module.GetDebugInfoOrNull(), |
|
|
|
|
}; |
|
|
|
|
if (options.EscapeInvalidIdentifiers) |
|
|
|
|
decompiler.AstTransforms.Add(new EscapeInvalidIdentifiers()); |
|
|
|
|
return decompiler; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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); |
|
|
|
|
MetadataFile assembly = method.ParentModule!.MetadataFile!; |
|
|
|
|
CSharpDecompiler decompiler = CreateDecompiler(assembly, options); |
|
|
|
|
AddReferenceAssemblyWarningMessage(assembly, output); |
|
|
|
|
AddReferenceWarningMessage(assembly, output); |
|
|
|
|
WriteCommentLine(output, assembly.FullName); |
|
|
|
|
WriteCommentLine(output, TypeToString(method.DeclaringType)); |
|
|
|
|
var methodDefinition = decompiler.TypeSystem.MainModule.ResolveEntity(method.MetadataToken) as IMethod; |
|
|
|
|
if (methodDefinition!.IsConstructor && methodDefinition.DeclaringType.IsReferenceType != false) |
|
|
|
|
{ |
|
|
|
|
var members = CollectFieldsAndCtors(methodDefinition.DeclaringTypeDefinition!, methodDefinition.IsStatic); |
|
|
|
|
decompiler.AstTransforms.Add(new SelectCtorTransform(methodDefinition)); |
|
|
|
|
WriteCode(output, options.DecompilerSettings, decompiler.Decompile(members), decompiler.TypeSystem); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
WriteCode(output, options.DecompilerSettings, decompiler.Decompile(method.MetadataToken), decompiler.TypeSystem); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public override void DecompileField(IField field, ITextOutput output, DecompilationOptions options) |
|
|
|
|
public override void DecompileProperty(IProperty property, ITextOutput output, DecompilationOptions options) |
|
|
|
|
{ |
|
|
|
|
Debug.Assert(field.ParentModule?.MetadataFile != null); |
|
|
|
|
DecompileEntities(field.ParentModule.MetadataFile, new[] { field.MetadataToken }, output, options); |
|
|
|
|
MetadataFile assembly = property.ParentModule!.MetadataFile!; |
|
|
|
|
CSharpDecompiler decompiler = CreateDecompiler(assembly, options); |
|
|
|
|
AddReferenceAssemblyWarningMessage(assembly, output); |
|
|
|
|
AddReferenceWarningMessage(assembly, output); |
|
|
|
|
WriteCommentLine(output, assembly.FullName); |
|
|
|
|
WriteCommentLine(output, TypeToString(property.DeclaringType)); |
|
|
|
|
WriteCode(output, options.DecompilerSettings, decompiler.Decompile(property.MetadataToken), decompiler.TypeSystem); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public override void DecompileProperty(IProperty property, ITextOutput output, DecompilationOptions options) |
|
|
|
|
public override void DecompileField(IField field, ITextOutput output, DecompilationOptions options) |
|
|
|
|
{ |
|
|
|
|
Debug.Assert(property.ParentModule?.MetadataFile != null); |
|
|
|
|
DecompileEntities(property.ParentModule.MetadataFile, new[] { property.MetadataToken }, output, options); |
|
|
|
|
MetadataFile assembly = field.ParentModule!.MetadataFile!; |
|
|
|
|
CSharpDecompiler decompiler = CreateDecompiler(assembly, options); |
|
|
|
|
AddReferenceAssemblyWarningMessage(assembly, output); |
|
|
|
|
AddReferenceWarningMessage(assembly, output); |
|
|
|
|
WriteCommentLine(output, assembly.FullName); |
|
|
|
|
WriteCommentLine(output, TypeToString(field.DeclaringType)); |
|
|
|
|
if (field.IsConst) |
|
|
|
|
{ |
|
|
|
|
WriteCode(output, options.DecompilerSettings, decompiler.Decompile(field.MetadataToken), decompiler.TypeSystem); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
var members = CollectFieldsAndCtors(field.DeclaringTypeDefinition!, field.IsStatic); |
|
|
|
|
var resolvedField = decompiler.TypeSystem.MainModule.GetDefinition((FieldDefinitionHandle)field.MetadataToken); |
|
|
|
|
decompiler.AstTransforms.Add(new SelectFieldTransform(resolvedField)); |
|
|
|
|
WriteCode(output, options.DecompilerSettings, decompiler.Decompile(members), decompiler.TypeSystem); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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); |
|
|
|
|
MetadataFile assembly = ev.ParentModule!.MetadataFile!; |
|
|
|
|
CSharpDecompiler decompiler = CreateDecompiler(assembly, options); |
|
|
|
|
AddReferenceAssemblyWarningMessage(assembly, output); |
|
|
|
|
AddReferenceWarningMessage(assembly, output); |
|
|
|
|
WriteCommentLine(output, assembly.FullName); |
|
|
|
|
WriteCommentLine(output, TypeToString(ev.DeclaringType)); |
|
|
|
|
WriteCode(output, options.DecompilerSettings, decompiler.Decompile(ev.MetadataToken), decompiler.TypeSystem); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public override void DecompileType(ITypeDefinition type, ITextOutput output, DecompilationOptions options) |
|
|
|
|
{ |
|
|
|
|
MetadataFile assembly = type.ParentModule!.MetadataFile!; |
|
|
|
|
CSharpDecompiler decompiler = CreateDecompiler(assembly, options); |
|
|
|
|
AddReferenceAssemblyWarningMessage(assembly, output); |
|
|
|
|
AddReferenceWarningMessage(assembly, output); |
|
|
|
|
WriteCommentLine(output, assembly.FullName); |
|
|
|
|
WriteCommentLine(output, TypeToString(type, ConversionFlags.UseFullyQualifiedTypeNames | ConversionFlags.UseFullyQualifiedEntityNames)); |
|
|
|
|
WriteCode(output, options.DecompilerSettings, decompiler.Decompile(type.MetadataToken), decompiler.TypeSystem); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public override void DecompileNamespace(string nameSpace, IEnumerable<ITypeDefinition> types, ITextOutput output, DecompilationOptions options) |
|
|
|
|
{ |
|
|
|
|
var typesByModule = types.GroupBy(t => { |
|
|
|
|
Debug.Assert(t.ParentModule?.MetadataFile != null); |
|
|
|
|
return t.ParentModule.MetadataFile; |
|
|
|
|
}); |
|
|
|
|
var typesByModule = types.GroupBy(t => 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); |
|
|
|
|
MetadataFile assembly = group.Key!; |
|
|
|
|
CSharpDecompiler decompiler = CreateDecompiler(assembly, options); |
|
|
|
|
AddReferenceAssemblyWarningMessage(assembly, output); |
|
|
|
|
AddReferenceWarningMessage(assembly, output); |
|
|
|
|
WriteCommentLine(output, assembly.FullName); |
|
|
|
|
WriteCommentLine(output, nameSpace); |
|
|
|
|
WriteCode(output, options.DecompilerSettings, decompiler.Decompile(group.Select(t => t.MetadataToken)), decompiler.TypeSystem); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void DecompileEntities(MetadataFile module, IEnumerable<EntityHandle> handles, ITextOutput output, DecompilationOptions options) |
|
|
|
|
public override ProjectId? DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) |
|
|
|
|
{ |
|
|
|
|
var module = assembly.GetMetadataFileOrNull(); |
|
|
|
|
if (module == null) |
|
|
|
|
return null; |
|
|
|
|
if (options.FullDecompilation && options.SaveAsProjectDirectory != null) |
|
|
|
|
throw new NotSupportedException($"Language '{Name}' does not support exporting assemblies as projects in the Avalonia host yet."); |
|
|
|
|
|
|
|
|
|
AddReferenceAssemblyWarningMessage(module, output); |
|
|
|
|
AddReferenceWarningMessage(module, output); |
|
|
|
|
output.WriteLine(); |
|
|
|
|
base.DecompileAssembly(assembly, output, options); |
|
|
|
|
|
|
|
|
|
var assemblyResolver = assembly.GetAssemblyResolver(loadOnDemand: options.FullDecompilation && options.DecompilerSettings.AutoLoadAssemblyReferences); |
|
|
|
|
var typeSystem = new DecompilerTypeSystem(module, assemblyResolver, options.DecompilerSettings); |
|
|
|
|
var globalType = typeSystem.MainModule.TypeDefinitions.FirstOrDefault(); |
|
|
|
|
if (globalType != null) |
|
|
|
|
{ |
|
|
|
|
WriteCommentLine(output, "(metadata file unavailable)"); |
|
|
|
|
return; |
|
|
|
|
output.Write("// Global type: "); |
|
|
|
|
output.WriteReference(globalType, ILAmbience.EscapeName(globalType.FullName)); |
|
|
|
|
output.WriteLine(); |
|
|
|
|
} |
|
|
|
|
var metadata = module.Metadata; |
|
|
|
|
var corHeader = module.CorHeader; |
|
|
|
|
if (module is PEFile peFile && corHeader != null) |
|
|
|
|
{ |
|
|
|
|
var entrypointHandle = MetadataTokenHelpers.EntityHandleOrNil(corHeader.EntryPointTokenOrRelativeVirtualAddress); |
|
|
|
|
if (!entrypointHandle.IsNil && entrypointHandle.Kind == HandleKind.MethodDefinition) |
|
|
|
|
{ |
|
|
|
|
var entrypoint = typeSystem.MainModule.ResolveMethod(entrypointHandle, new GenericContext()); |
|
|
|
|
if (entrypoint != null) |
|
|
|
|
{ |
|
|
|
|
output.Write("// Entry point: "); |
|
|
|
|
output.WriteReference(entrypoint, ILAmbience.EscapeName(entrypoint.DeclaringType.FullName + "." + entrypoint.Name)); |
|
|
|
|
output.WriteLine(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
output.WriteLine("// Architecture: " + GetPlatformDisplayName(peFile)); |
|
|
|
|
if ((corHeader.Flags & CorFlags.ILOnly) == 0) |
|
|
|
|
output.WriteLine("// This assembly contains unmanaged code."); |
|
|
|
|
string runtimeName = GetRuntimeDisplayName(module); |
|
|
|
|
if (runtimeName != null) |
|
|
|
|
output.WriteLine("// Runtime: " + runtimeName); |
|
|
|
|
if ((corHeader.Flags & CorFlags.StrongNameSigned) != 0) |
|
|
|
|
output.WriteLine("// This assembly is signed with a strong name key."); |
|
|
|
|
if (peFile.Reader.ReadDebugDirectory().Any(d => d.Type == DebugDirectoryEntryType.Reproducible)) |
|
|
|
|
output.WriteLine("// This assembly was compiled using the /deterministic option."); |
|
|
|
|
if (module.Metadata.MetadataKind != MetadataKind.Ecma335) |
|
|
|
|
output.WriteLine("// This assembly was loaded with Windows Runtime projections applied."); |
|
|
|
|
} |
|
|
|
|
var resolver = module.GetAssemblyResolver(options.DecompilerSettings.AutoLoadAssemblyReferences); |
|
|
|
|
var decompiler = new CSharpDecompiler(module, resolver, options.DecompilerSettings) { |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
string runtimeName = GetRuntimeDisplayName(module); |
|
|
|
|
if (runtimeName != null) |
|
|
|
|
output.WriteLine("// Runtime: " + runtimeName); |
|
|
|
|
} |
|
|
|
|
if (metadata.IsAssembly) |
|
|
|
|
{ |
|
|
|
|
var asm = metadata.GetAssemblyDefinition(); |
|
|
|
|
if (asm.HashAlgorithm != System.Reflection.AssemblyHashAlgorithm.None) |
|
|
|
|
output.WriteLine("// Hash algorithm: " + asm.HashAlgorithm.ToString().ToUpper()); |
|
|
|
|
if (!asm.PublicKey.IsNil) |
|
|
|
|
{ |
|
|
|
|
output.Write("// Public key: "); |
|
|
|
|
var reader = metadata.GetBlobReader(asm.PublicKey); |
|
|
|
|
while (reader.RemainingBytes > 0) |
|
|
|
|
output.Write(reader.ReadByte().ToString("x2")); |
|
|
|
|
output.WriteLine(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
var debugInfo = assembly.GetDebugInfoOrNull(); |
|
|
|
|
if (debugInfo != null) |
|
|
|
|
output.WriteLine("// Debug info: " + debugInfo.Description); |
|
|
|
|
output.WriteLine(); |
|
|
|
|
|
|
|
|
|
var decompiler = new CSharpDecompiler(typeSystem, options.DecompilerSettings) { |
|
|
|
|
CancellationToken = options.CancellationToken, |
|
|
|
|
DebugInfoProvider = module.GetDebugInfoOrNull(), |
|
|
|
|
}; |
|
|
|
|
SyntaxTree syntaxTree = decompiler.Decompile(handles); |
|
|
|
|
WriteCode(output, options.DecompilerSettings, syntaxTree, decompiler.TypeSystem); |
|
|
|
|
if (options.EscapeInvalidIdentifiers) |
|
|
|
|
decompiler.AstTransforms.Add(new EscapeInvalidIdentifiers()); |
|
|
|
|
SyntaxTree st = options.FullDecompilation |
|
|
|
|
? decompiler.DecompileWholeModuleAsSingleFile() |
|
|
|
|
: decompiler.DecompileModuleAndAssemblyAttributes(); |
|
|
|
|
WriteCode(output, options.DecompilerSettings, st, decompiler.TypeSystem); |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static List<EntityHandle> CollectFieldsAndCtors(ITypeDefinition type, bool isStatic) |
|
|
|
|
{ |
|
|
|
|
var members = new List<EntityHandle>(); |
|
|
|
|
foreach (var field in type.Fields) |
|
|
|
|
if (!field.MetadataToken.IsNil && field.IsStatic == isStatic) |
|
|
|
|
members.Add(field.MetadataToken); |
|
|
|
|
foreach (var e in type.Events) |
|
|
|
|
if (!e.MetadataToken.IsNil && e.IsStatic == isStatic) |
|
|
|
|
members.Add(e.MetadataToken); |
|
|
|
|
foreach (var p in type.Properties) |
|
|
|
|
if (!p.MetadataToken.IsNil && p.IsStatic == isStatic && !p.IsIndexer) |
|
|
|
|
members.Add(p.MetadataToken); |
|
|
|
|
foreach (var ctor in type.Methods) |
|
|
|
|
if (!ctor.MetadataToken.IsNil && ctor.IsConstructor && ctor.IsStatic == isStatic) |
|
|
|
|
members.Add(ctor.MetadataToken); |
|
|
|
|
return members; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
sealed class SelectCtorTransform(IMethod ctor) : IAstTransform |
|
|
|
|
{ |
|
|
|
|
readonly HashSet<ISymbol?> removedSymbols = new(); |
|
|
|
|
|
|
|
|
|
public void Run(AstNode rootNode, TransformContext context) |
|
|
|
|
{ |
|
|
|
|
ConstructorDeclaration? ctorDecl = null; |
|
|
|
|
foreach (var node in rootNode.Children) |
|
|
|
|
{ |
|
|
|
|
switch (node) |
|
|
|
|
{ |
|
|
|
|
case ConstructorDeclaration cd: |
|
|
|
|
if (cd.GetSymbol() == ctor) |
|
|
|
|
ctorDecl = cd; |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
cd.Remove(); |
|
|
|
|
removedSymbols.Add(cd.GetSymbol()); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case FieldDeclaration fd: |
|
|
|
|
if (fd.Variables.All(v => v.Initializer.IsNull)) |
|
|
|
|
{ |
|
|
|
|
fd.Remove(); |
|
|
|
|
removedSymbols.Add(fd.GetSymbol()); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case EventDeclaration ed: |
|
|
|
|
if (ed.Variables.All(v => v.Initializer.IsNull)) |
|
|
|
|
{ |
|
|
|
|
ed.Remove(); |
|
|
|
|
removedSymbols.Add(ed.GetSymbol()); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case PropertyDeclaration pd: |
|
|
|
|
if (pd.Initializer.IsNull) |
|
|
|
|
{ |
|
|
|
|
pd.Remove(); |
|
|
|
|
removedSymbols.Add(pd.GetSymbol()); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case CustomEventDeclaration: |
|
|
|
|
case IndexerDeclaration: |
|
|
|
|
node.Remove(); |
|
|
|
|
removedSymbols.Add(node.GetSymbol()); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (ctorDecl?.Initializer.ConstructorInitializerType == ConstructorInitializerType.This) |
|
|
|
|
{ |
|
|
|
|
foreach (var node in rootNode.Children) |
|
|
|
|
{ |
|
|
|
|
if (node is not ConstructorDeclaration) |
|
|
|
|
{ |
|
|
|
|
node.Remove(); |
|
|
|
|
removedSymbols.Add(node.GetSymbol()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
foreach (var node in rootNode.Children) |
|
|
|
|
{ |
|
|
|
|
if (node is Comment && removedSymbols.Contains(node.GetSymbol())) |
|
|
|
|
node.Remove(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
sealed class SelectFieldTransform(IField field) : IAstTransform |
|
|
|
|
{ |
|
|
|
|
public void Run(AstNode rootNode, TransformContext context) |
|
|
|
|
{ |
|
|
|
|
foreach (var node in rootNode.Children) |
|
|
|
|
{ |
|
|
|
|
switch (node) |
|
|
|
|
{ |
|
|
|
|
case EntityDeclaration: |
|
|
|
|
if (node.GetSymbol() != field) |
|
|
|
|
node.Remove(); |
|
|
|
|
break; |
|
|
|
|
case Comment c: |
|
|
|
|
if (c.GetSymbol() != field) |
|
|
|
|
node.Remove(); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void WriteCode(ITextOutput output, DecompilerSettings settings, SyntaxTree syntaxTree, IDecompilerTypeSystem typeSystem) |
|
|
|
|
@ -159,12 +394,84 @@ namespace ILSpy.Languages
@@ -159,12 +394,84 @@ namespace ILSpy.Languages
|
|
|
|
|
syntaxTree.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true }); |
|
|
|
|
output.IndentationString = settings.CSharpFormattingOptions.IndentationString; |
|
|
|
|
TokenWriter tokenWriter = new TextTokenWriter(output, settings, typeSystem); |
|
|
|
|
// Wrap with semantic highlighting when the output supports it. The writer emits
|
|
|
|
|
// BeginSpan/EndSpan around tokens, which AvaloniaEditTextOutput records into a
|
|
|
|
|
// RichTextModel that the view's RichTextColorizer paints.
|
|
|
|
|
if (output is TextView.ISmartTextOutput smartOutput) |
|
|
|
|
tokenWriter = new CSharpHighlightingTokenWriter(tokenWriter, smartOutput); |
|
|
|
|
syntaxTree.AcceptVisitor(new CSharpOutputVisitor(tokenWriter, settings.CSharpFormattingOptions)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void AddWarningMessage(MetadataFile module, ITextOutput output, string line1, string? line2 = null, |
|
|
|
|
string? buttonText = null, global::Avalonia.Media.IImage? buttonImage = null, |
|
|
|
|
System.EventHandler<global::Avalonia.Interactivity.RoutedEventArgs>? buttonClickHandler = null) |
|
|
|
|
{ |
|
|
|
|
if (output is TextView.ISmartTextOutput fancyOutput) |
|
|
|
|
{ |
|
|
|
|
string text = line1; |
|
|
|
|
if (!string.IsNullOrEmpty(line2)) |
|
|
|
|
text += System.Environment.NewLine + line2; |
|
|
|
|
fancyOutput.AddUIElement(() => new global::Avalonia.Controls.StackPanel { |
|
|
|
|
Margin = new global::Avalonia.Thickness(5), |
|
|
|
|
Orientation = global::Avalonia.Layout.Orientation.Horizontal, |
|
|
|
|
Children = { |
|
|
|
|
new global::Avalonia.Controls.Image { |
|
|
|
|
Width = 32, |
|
|
|
|
Height = 32, |
|
|
|
|
Source = Images.Images.Warning, |
|
|
|
|
}, |
|
|
|
|
new global::Avalonia.Controls.TextBlock { |
|
|
|
|
Margin = new global::Avalonia.Thickness(5, 0, 0, 0), |
|
|
|
|
Text = text, |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
}); |
|
|
|
|
fancyOutput.WriteLine(); |
|
|
|
|
if (buttonText != null && buttonClickHandler != null) |
|
|
|
|
{ |
|
|
|
|
fancyOutput.AddButton(buttonImage, buttonText, buttonClickHandler); |
|
|
|
|
fancyOutput.WriteLine(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
WriteCommentLine(output, line1); |
|
|
|
|
if (!string.IsNullOrEmpty(line2)) |
|
|
|
|
WriteCommentLine(output, line2); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void AddReferenceAssemblyWarningMessage(MetadataFile module, ITextOutput output) |
|
|
|
|
{ |
|
|
|
|
var metadata = module.Metadata; |
|
|
|
|
if (!metadata.GetCustomAttributes(Handle.AssemblyDefinition).HasKnownAttribute(metadata, KnownAttribute.ReferenceAssembly)) |
|
|
|
|
return; |
|
|
|
|
AddWarningMessage(module, output, Resources.WarningAsmMarkedRef); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void AddReferenceWarningMessage(MetadataFile module, ITextOutput output) |
|
|
|
|
{ |
|
|
|
|
// Resolving AssemblyTreeModel via composition would create a circular registration
|
|
|
|
|
// (LanguageService → Language → AssemblyTreeModel → LanguageService). Match the WPF
|
|
|
|
|
// output by looking the assembly up directly off the module — same predicate (the
|
|
|
|
|
// metadata file equality), no service dependency.
|
|
|
|
|
if (!HasReferenceErrors(module)) |
|
|
|
|
return; |
|
|
|
|
AddWarningMessage(module, output, |
|
|
|
|
Resources.WarningSomeAssemblyReference, |
|
|
|
|
Resources.PropertyManuallyMissingReferencesListLoadedAssemblies); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static bool HasReferenceErrors(MetadataFile module) |
|
|
|
|
{ |
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
var atm = AppEnv.AppComposition.Current.GetExport<AssemblyTree.AssemblyTreeModel>(); |
|
|
|
|
var loadedAssembly = atm.AssemblyList?.GetAssemblies() |
|
|
|
|
.FirstOrDefault(la => la.GetMetadataFileOrNull() == module); |
|
|
|
|
return loadedAssembly?.LoadedAssemblyReferencesInfo.HasErrors == true; |
|
|
|
|
} |
|
|
|
|
catch |
|
|
|
|
{ |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|