diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index 0c50975b4..2a2af5d2c 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -50,7 +50,7 @@ namespace ICSharpCode.Decompiler.Ast DoNotUsePrimitiveTypeNames = 4 } - public class AstBuilder : ICodeMappings + public class AstBuilder : BaseCodeMappings { DecompilerContext context; CompilationUnit astCompileUnit = new CompilationUnit(); @@ -65,6 +65,8 @@ namespace ICSharpCode.Decompiler.Ast this.DecompileMethodBodies = true; this.LocalVariables = new ConcurrentDictionary>(); + this.CodeMappings = new Dictionary>(); + this.DecompiledMemberReferences = new Dictionary(); } public static bool MemberIsHidden(MemberReference member, DecompilerSettings settings) @@ -259,10 +261,6 @@ namespace ICSharpCode.Decompiler.Ast /// TypeDeclaration or DelegateDeclaration. public AttributedNode CreateType(TypeDefinition typeDef) { - // create CSharp code mappings - used for debugger - if (this.CodeMappings == null) - this.CodeMappings = new Tuple>(typeDef.FullName, new List()); - // create type TypeDefinition oldCurrentType = context.CurrentType; context.CurrentType = typeDef; @@ -732,7 +730,8 @@ namespace ICSharpCode.Decompiler.Ast AttributedNode CreateMethod(MethodDefinition methodDef) { // Create mapping - used in debugger - MemberMapping methodMapping = methodDef.CreateCodeMapping(this.CodeMappings); + CreateCodeMappings(methodDef.MetadataToken.ToInt32(), methodDef); + MemberMapping methodMapping = methodDef.CreateCodeMapping(this.CodeMappings[methodDef.MetadataToken.ToInt32()]); MethodDeclaration astMethod = new MethodDeclaration().WithAnnotation(methodMapping); astMethod.AddAnnotation(methodDef); @@ -820,7 +819,8 @@ namespace ICSharpCode.Decompiler.Ast ConstructorDeclaration CreateConstructor(MethodDefinition methodDef) { // Create mapping - used in debugger - MemberMapping methodMapping = methodDef.CreateCodeMapping(this.CodeMappings); + CreateCodeMappings(methodDef.MetadataToken.ToInt32(), methodDef); + MemberMapping methodMapping = methodDef.CreateCodeMapping(this.CodeMappings[methodDef.MetadataToken.ToInt32()]); ConstructorDeclaration astMethod = new ConstructorDeclaration(); astMethod.AddAnnotation(methodDef); @@ -884,9 +884,11 @@ namespace ICSharpCode.Decompiler.Ast } astProp.Name = CleanName(propDef.Name); astProp.ReturnType = ConvertType(propDef.PropertyType, propDef); + if (propDef.GetMethod != null) { // Create mapping - used in debugger - MemberMapping methodMapping = propDef.GetMethod.CreateCodeMapping(this.CodeMappings); + CreateCodeMappings(propDef.GetMethod.MetadataToken.ToInt32(), propDef); + MemberMapping methodMapping = propDef.GetMethod.CreateCodeMapping(this.CodeMappings[propDef.GetMethod.MetadataToken.ToInt32()], propDef); astProp.Getter = new Accessor(); astProp.Getter.Body = CreateMethodBody(propDef.GetMethod); @@ -900,7 +902,8 @@ namespace ICSharpCode.Decompiler.Ast } if (propDef.SetMethod != null) { // Create mapping - used in debugger - MemberMapping methodMapping = propDef.SetMethod.CreateCodeMapping(this.CodeMappings); + CreateCodeMappings(propDef.SetMethod.MetadataToken.ToInt32(), propDef); + MemberMapping methodMapping = propDef.SetMethod.CreateCodeMapping(this.CodeMappings[propDef.SetMethod.MetadataToken.ToInt32()], propDef); astProp.Setter = new Accessor(); astProp.Setter.Body = CreateMethodBody(propDef.SetMethod); @@ -967,9 +970,11 @@ namespace ICSharpCode.Decompiler.Ast astEvent.Modifiers = ConvertModifiers(eventDef.AddMethod); else astEvent.PrivateImplementationType = ConvertType(eventDef.AddMethod.Overrides.First().DeclaringType); + if (eventDef.AddMethod != null) { // Create mapping - used in debugger - MemberMapping methodMapping = eventDef.AddMethod.CreateCodeMapping(this.CodeMappings); + CreateCodeMappings(eventDef.AddMethod.MetadataToken.ToInt32(), eventDef); + MemberMapping methodMapping = eventDef.AddMethod.CreateCodeMapping(this.CodeMappings[eventDef.AddMethod.MetadataToken.ToInt32()], eventDef); astEvent.AddAccessor = new Accessor { Body = CreateMethodBody(eventDef.AddMethod) @@ -980,7 +985,8 @@ namespace ICSharpCode.Decompiler.Ast } if (eventDef.RemoveMethod != null) { // Create mapping - used in debugger - MemberMapping methodMapping = eventDef.RemoveMethod.CreateCodeMapping(this.CodeMappings); + CreateCodeMappings(eventDef.RemoveMethod.MetadataToken.ToInt32(), eventDef); + MemberMapping methodMapping = eventDef.RemoveMethod.CreateCodeMapping(this.CodeMappings[eventDef.RemoveMethod.MetadataToken.ToInt32()], eventDef); astEvent.RemoveAccessor = new Accessor { Body = CreateMethodBody(eventDef.RemoveMethod) @@ -1009,6 +1015,8 @@ namespace ICSharpCode.Decompiler.Ast FieldDeclaration CreateField(FieldDefinition fieldDef) { + this.DecompiledMemberReferences.Add(fieldDef.MetadataToken.ToInt32(), fieldDef); + FieldDeclaration astField = new FieldDeclaration(); astField.AddAnnotation(fieldDef); VariableInitializer initializer = new VariableInitializer(CleanName(fieldDef.Name)); @@ -1151,7 +1159,7 @@ namespace ICSharpCode.Decompiler.Ast MethodImplAttributes implAttributes = methodDefinition.ImplAttributes & ~MethodImplAttributes.CodeTypeMask; #region DllImportAttribute - if (methodDefinition.HasPInvokeInfo) { + if (methodDefinition.HasPInvokeInfo && methodDefinition.PInvokeInfo != null) { PInvokeInfo info = methodDefinition.PInvokeInfo; Ast.Attribute dllImport = CreateNonCustomAttribute(typeof(DllImportAttribute)); dllImport.Arguments.Add(new PrimitiveExpression(info.Module.Name)); @@ -1501,13 +1509,13 @@ namespace ICSharpCode.Decompiler.Ast { // cannot rely on type.IsValueType, it's not set for typerefs (but is set for typespecs) TypeDefinition enumDefinition = type.Resolve(); if (enumDefinition != null && enumDefinition.IsEnum) { + TypeCode enumBaseTypeCode = TypeCode.Int32; foreach (FieldDefinition field in enumDefinition.Fields) { if (field.IsStatic && object.Equals(CSharpPrimitiveCast.Cast(TypeCode.Int64, field.Constant, false), val)) - return ConvertType(enumDefinition).Member(field.Name).WithAnnotation(field); + return ConvertType(type).Member(field.Name).WithAnnotation(field); else if (!field.IsStatic && field.IsRuntimeSpecialName) - type = field.FieldType; // use primitive type of the enum + enumBaseTypeCode = TypeAnalysis.GetTypeCode(field.FieldType); // use primitive type of the enum } - TypeCode enumBaseTypeCode = TypeAnalysis.GetTypeCode(type); if (IsFlagsEnum(enumDefinition)) { long enumValue = val; Expression expr = null; @@ -1534,7 +1542,7 @@ namespace ICSharpCode.Decompiler.Ast continue; // skip None enum value if ((fieldValue & enumValue) == fieldValue) { - var fieldExpression = ConvertType(enumDefinition).Member(field.Name).WithAnnotation(field); + var fieldExpression = ConvertType(type).Member(field.Name).WithAnnotation(field); if (expr == null) expr = fieldExpression; else @@ -1543,7 +1551,7 @@ namespace ICSharpCode.Decompiler.Ast enumValue &= ~fieldValue; } if ((fieldValue & negatedEnumValue) == fieldValue) { - var fieldExpression = ConvertType(enumDefinition).Member(field.Name).WithAnnotation(field); + var fieldExpression = ConvertType(type).Member(field.Name).WithAnnotation(field); if (negatedExpr == null) negatedExpr = fieldExpression; else @@ -1561,7 +1569,7 @@ namespace ICSharpCode.Decompiler.Ast return new UnaryOperatorExpression(UnaryOperatorType.BitNot, negatedExpr); } } - return new Ast.PrimitiveExpression(CSharpPrimitiveCast.Cast(enumBaseTypeCode, val, false)).CastTo(ConvertType(enumDefinition)); + return new Ast.PrimitiveExpression(CSharpPrimitiveCast.Cast(enumBaseTypeCode, val, false)).CastTo(ConvertType(type)); } } TypeCode code = TypeAnalysis.GetTypeCode(type); @@ -1655,11 +1663,6 @@ namespace ICSharpCode.Decompiler.Ast && TypesHierarchyHelpers.IsVisibleFromDerived(m, derived.DeclaringType)); } - /// - /// - /// - public Tuple> CodeMappings { get; private set; } - /// /// Gets the local variables for the current decompiled type, method, etc. /// The key is the metadata token. diff --git a/ICSharpCode.Decompiler/Ast/NameVariables.cs b/ICSharpCode.Decompiler/Ast/NameVariables.cs index 129fd3e54..52d887e3f 100644 --- a/ICSharpCode.Decompiler/Ast/NameVariables.cs +++ b/ICSharpCode.Decompiler/Ast/NameVariables.cs @@ -288,6 +288,8 @@ namespace ICSharpCode.Decompiler.Ast string GetNameByType(TypeReference type) { + type = TypeAnalysis.UnpackModifiers(type); + GenericInstanceType git = type as GenericInstanceType; if (git != null && git.ElementType.FullName == "System.Nullable`1" && git.GenericArguments.Count == 1) { type = ((GenericInstanceType)type).GenericArguments[0]; diff --git a/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs b/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs index b9b851bb7..7c8ca1230 100644 --- a/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs +++ b/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs @@ -148,9 +148,9 @@ namespace ICSharpCode.Decompiler.Ast public void StartNode(AstNode node) { + // code mappings var ranges = node.Annotation>(); - if (ranges != null && ranges.Count > 0) - { + if (ranges != null && ranges.Count > 0) { // find the ancestor that has method mapping as annotation if (node.Parent != null) { @@ -170,6 +170,17 @@ namespace ICSharpCode.Decompiler.Ast } } + // definitions of types and their members + Predicate predicate = n => n is AttributedNode; + + if (predicate(node)) { + var n = node as AttributedNode; + int c = 0; + if (n != null) + c = n.Attributes.Count; + node.AddAnnotation(Tuple.Create(output.CurrentLine + c, output.CurrentColumn)); + } + nodeStack.Push(node); } diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs index 85ac9da1e..0e431e3f8 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs @@ -136,6 +136,9 @@ namespace ICSharpCode.Decompiler.Ast.Transforms // Create AnonymousMethodExpression and prepare parameters AnonymousMethodExpression ame = new AnonymousMethodExpression(); + ame.CopyAnnotationsFrom(objectCreateExpression); // copy ILRanges etc. + ame.RemoveAnnotations(); // remove reference to delegate ctor + ame.AddAnnotation(method); // add reference to anonymous method ame.Parameters.AddRange(AstBuilder.MakeParameters(method, isLambda: true)); ame.HasParameterList = true; @@ -180,6 +183,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms } if (isLambda) { LambdaExpression lambda = new LambdaExpression(); + lambda.CopyAnnotationsFrom(ame); ame.Parameters.MoveTo(lambda.Parameters); Expression returnExpr = ((ReturnStatement)body.Statements.Single()).Expression; returnExpr.Remove(); diff --git a/ICSharpCode.Decompiler/CodeMappings.cs b/ICSharpCode.Decompiler/CodeMappings.cs index 57c9c6979..485792f4a 100644 --- a/ICSharpCode.Decompiler/CodeMappings.cs +++ b/ICSharpCode.Decompiler/CodeMappings.cs @@ -35,14 +35,36 @@ namespace ICSharpCode.Decompiler } /// - /// Interface for decompliler classes : AstBuilder & ReflectionDisassembler. + /// Base class for decompliler classes : AstBuilder & ReflectionDisassembler. /// - public interface ICodeMappings + public abstract class BaseCodeMappings { /// /// Gets the code mappings. + /// Key is the metadata token. /// - Tuple> CodeMappings { get; } + public Dictionary> CodeMappings { get; protected set; } + + /// + /// Gets the MembeReference that is decompiled (a MethodDefinition, PropertyDefinition etc.) + /// Key is the metadata token. + /// + public Dictionary DecompiledMemberReferences { get; protected set; } + + /// + /// Create data in the CodeMappings and DecompiledMemberReferences. + /// + /// Token of the current method. + /// Current member (MethodDefinition, PropertyDefinition, EventDefinition). + /// The token is used in CodeMappings; member (and its token) is used in DecompiledMemberReferences. + protected virtual void CreateCodeMappings(int token, MemberReference member) + { + this.CodeMappings.Add(token, new List()); + + int t = member.MetadataToken.ToInt32(); + if (!this.DecompiledMemberReferences.ContainsKey(t)) + this.DecompiledMemberReferences.Add(t, member); + } } /// @@ -112,9 +134,9 @@ namespace ICSharpCode.Decompiler public MemberReference MemberReference { get; internal set; } /// - /// Metadata token of the method. + /// Metadata token of the member. /// - public uint MetadataToken { get; internal set; } + public int MetadataToken { get; internal set; } /// /// Gets or sets the code size for the member mapping. @@ -148,15 +170,17 @@ namespace ICSharpCode.Decompiler /// Code mappings helper class. /// public static class CodeMappings - { + { /// /// Create code mapping for a method. /// /// Method to create the mapping for. - /// Source code mapping storage. + /// Source code mapping storage. + /// The actual member reference. internal static MemberMapping CreateCodeMapping( this MethodDefinition member, - Tuple> codeMappings) + List codeMappings, + MemberReference actualMemberReference = null) { if (member == null || !member.HasBody) return null; @@ -166,17 +190,16 @@ namespace ICSharpCode.Decompiler // create IL/CSharp code mappings - used in debugger MemberMapping currentMemberMapping = null; - if (codeMappings.Item1 == member.DeclaringType.FullName) { - var mapping = codeMappings.Item2; - if (mapping.Find(map => (int)map.MetadataToken == member.MetadataToken.ToInt32()) == null) { - currentMemberMapping = new MemberMapping() { - MetadataToken = (uint)member.MetadataToken.ToInt32(), - MemberReference = member.DeclaringType.Resolve(), - MemberCodeMappings = new List(), - CodeSize = member.Body.CodeSize - }; - mapping.Add(currentMemberMapping); - } + + if (codeMappings.Find(map => map.MetadataToken == member.MetadataToken.ToInt32()) == null) { + currentMemberMapping = new MemberMapping() { + MetadataToken = member.MetadataToken.ToInt32(), + MemberCodeMappings = new List(), + MemberReference = actualMemberReference ?? member, + CodeSize = member.Body.CodeSize + }; + + codeMappings.Add(currentMemberMapping); } return currentMemberMapping; @@ -186,31 +209,19 @@ namespace ICSharpCode.Decompiler /// Gets source code mapping and metadata token based on type name and line number. /// /// Code mappings storage. - /// Type name. + /// Member reference name. /// Line number. /// Metadata token. /// - public static SourceCodeMapping GetInstructionByTypeAndLine( - this Tuple> codeMappings, - string memberReferenceName, + public static SourceCodeMapping GetInstructionByLineNumber( + this List codeMappings, int lineNumber, - out uint metadataToken) + out int metadataToken) { if (codeMappings == null) - throw new ArgumentNullException("CodeMappings storage must be valid!"); + throw new ArgumentException("CodeMappings storage must be valid!"); - if (codeMappings.Item1 != memberReferenceName) { - metadataToken = 0; - return null; - } - - if (lineNumber <= 0) { - metadataToken = 0; - return null; - } - - var methodMappings = codeMappings.Item2; - foreach (var maping in methodMappings) { + foreach (var maping in codeMappings) { var map = maping.MemberCodeMappings.Find(m => m.SourceCodeLine == lineNumber); if (map != null) { metadataToken = maping.MetadataToken; @@ -226,40 +237,32 @@ namespace ICSharpCode.Decompiler /// Gets a mapping given a type, a token and an IL offset. /// /// Code mappings storage. - /// Type name. /// Token. /// IL offset. /// True, if perfect match. /// A code mapping. - public static SourceCodeMapping GetInstructionByTypeTokenAndOffset( - this Tuple> codeMappings, - string memberReferenceName, - uint token, - int ilOffset, out bool isMatch) + public static SourceCodeMapping GetInstructionByTokenAndOffset( + this List codeMappings, + int token, + int ilOffset, + out bool isMatch) { isMatch = false; - memberReferenceName = memberReferenceName.Replace("+", "/"); if (codeMappings == null) throw new ArgumentNullException("CodeMappings storage must be valid!"); - if (codeMappings.Item1 != memberReferenceName) { - return null; - } - - var methodMappings = codeMappings.Item2; - var maping = methodMappings.Find(m => m.MetadataToken == token); + var maping = codeMappings.Find(m => m.MetadataToken == token); - if (maping == null) { + if (maping == null) return null; - } // try find an exact match var map = maping.MemberCodeMappings.Find(m => m.ILInstructionOffset.From <= ilOffset && ilOffset < m.ILInstructionOffset.To); if (map == null) { // get the immediate next one - map = maping.MemberCodeMappings.Find(m => m.ILInstructionOffset.From >= ilOffset); + map = maping.MemberCodeMappings.Find(m => m.ILInstructionOffset.From > ilOffset); isMatch = false; if (map == null) map = maping.MemberCodeMappings.LastOrDefault(); // get the last @@ -275,38 +278,32 @@ namespace ICSharpCode.Decompiler /// Gets the source code and type name from metadata token and offset. /// /// Code mappings storage. - /// Current type name. /// Metadata token. /// IL offset. /// Type definition. /// Line number. /// It is possible to exist to different types from different assemblies with the same metadata token. - public static bool GetSourceCodeFromMetadataTokenAndOffset( - this Tuple> codeMappings, - string memberReferenceName, - uint token, + public static bool GetInstructionByTokenAndOffset( + this List codeMappings, + int token, int ilOffset, - out MemberReference type, + out MemberReference member, out int line) { - type = null; + member = null; line = 0; if (codeMappings == null) - throw new ArgumentNullException("CodeMappings storage must be valid!"); - - memberReferenceName = memberReferenceName.Replace("+", "/"); - if (codeMappings.Item1 != memberReferenceName) - return false; + throw new ArgumentException("CodeMappings storage must be valid!"); - var mapping = codeMappings.Item2.Find(m => m.MetadataToken == token); + var mapping = codeMappings.Find(m => m.MetadataToken == token); if (mapping == null) return false; var codeMapping = mapping.MemberCodeMappings.Find( cm => cm.ILInstructionOffset.From <= ilOffset && ilOffset <= cm.ILInstructionOffset.To - 1); if (codeMapping == null) { - codeMapping = mapping.MemberCodeMappings.Find(cm => (cm.ILInstructionOffset.From >= ilOffset)); + codeMapping = mapping.MemberCodeMappings.Find(cm => cm.ILInstructionOffset.From > ilOffset); if (codeMapping == null) { codeMapping = mapping.MemberCodeMappings.LastOrDefault(); if (codeMapping == null) @@ -314,7 +311,7 @@ namespace ICSharpCode.Decompiler } } - type = mapping.MemberReference; + member = mapping.MemberReference; line = codeMapping.SourceCodeLine; return true; } diff --git a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs index 4e144924e..2ced5f8d9 100644 --- a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs @@ -30,12 +30,13 @@ namespace ICSharpCode.Decompiler.Disassembler /// /// Disassembles type and member definitions. /// - public sealed class ReflectionDisassembler : ICodeMappings + public sealed class ReflectionDisassembler : BaseCodeMappings { ITextOutput output; CancellationToken cancellationToken; bool isInType; // whether we are currently disassembling a whole type (-> defaultCollapsed for foldings) MethodBodyDisassembler methodBodyDisassembler; + MemberReference currentMember; public ReflectionDisassembler(ITextOutput output, bool detectControlStructure, CancellationToken cancellationToken) { @@ -44,6 +45,9 @@ namespace ICSharpCode.Decompiler.Disassembler this.output = output; this.cancellationToken = cancellationToken; this.methodBodyDisassembler = new MethodBodyDisassembler(output, detectControlStructure, cancellationToken); + + this.CodeMappings = new Dictionary>(); + this.DecompiledMemberReferences = new Dictionary(); } #region Disassemble Method @@ -99,6 +103,9 @@ namespace ICSharpCode.Decompiler.Disassembler public void DisassembleMethod(MethodDefinition method) { + // set current member + currentMember = method; + // write method header output.WriteDefinition(".method ", method); DisassembleMethodInternal(method); @@ -117,7 +124,7 @@ namespace ICSharpCode.Decompiler.Disassembler if ((method.Attributes & MethodAttributes.PInvokeImpl) == MethodAttributes.PInvokeImpl) { output.Write("pinvokeimpl"); - if (method.HasPInvokeInfo) { + if (method.HasPInvokeInfo && method.PInvokeInfo != null) { PInvokeInfo info = method.PInvokeInfo; output.Write("(\"" + NRefactory.CSharp.OutputVisitor.ConvertString(info.Module.Name) + "\""); @@ -164,7 +171,6 @@ namespace ICSharpCode.Decompiler.Disassembler //call convention WriteEnum(method.CallingConvention & (MethodCallingConvention)0x1f, callingConvention); - //return type method.ReturnType.WriteTo(output); output.Write(' '); @@ -214,7 +220,8 @@ namespace ICSharpCode.Decompiler.Disassembler if (method.HasBody) { // create IL code mappings - used in debugger - MemberMapping methodMapping = method.CreateCodeMapping(this.CodeMappings); + CreateCodeMappings(method.MetadataToken.ToInt32(), currentMember); + MemberMapping methodMapping = method.CreateCodeMapping(this.CodeMappings[method.MetadataToken.ToInt32()], currentMember); methodBodyDisassembler.Disassemble(method.Body, methodMapping); } @@ -669,6 +676,9 @@ namespace ICSharpCode.Decompiler.Disassembler public void DisassembleField(FieldDefinition field) { + // create mappings for decompiled fields only + this.DecompiledMemberReferences.Add(field.MetadataToken.ToInt32(), field); + output.WriteDefinition(".field ", field); WriteEnum(field.Attributes & FieldAttributes.FieldAccessMask, fieldVisibility); const FieldAttributes hasXAttributes = FieldAttributes.HasDefault | FieldAttributes.HasFieldMarshal | FieldAttributes.HasFieldRVA; @@ -704,6 +714,9 @@ namespace ICSharpCode.Decompiler.Disassembler public void DisassembleProperty(PropertyDefinition property) { + // set current member + currentMember = property; + output.WriteDefinition(".property ", property); WriteFlags(property.Attributes, propertyAttributes); if (property.HasThis) @@ -725,6 +738,7 @@ namespace ICSharpCode.Decompiler.Disassembler WriteAttributes(property.CustomAttributes); WriteNestedMethod(".get", property.GetMethod); WriteNestedMethod(".set", property.SetMethod); + foreach (var method in property.OtherMethods) { WriteNestedMethod(".other", method); } @@ -750,6 +764,9 @@ namespace ICSharpCode.Decompiler.Disassembler public void DisassembleEvent(EventDefinition ev) { + // set current member + currentMember = ev; + output.WriteDefinition(".event ", ev); WriteFlags(ev.Attributes, eventAttributes); ev.EventType.WriteTo(output, ILNameSyntax.TypeName); @@ -803,10 +820,6 @@ namespace ICSharpCode.Decompiler.Disassembler public void DisassembleType(TypeDefinition type) { - // create IL code mappings - used for debugger - if (this.CodeMappings == null) - this.CodeMappings = new Tuple>(type.FullName, new List()); - // start writing IL output.WriteDefinition(".class ", type); @@ -1141,11 +1154,5 @@ namespace ICSharpCode.Decompiler.Disassembler output.WriteLine(); } } - - /// - public Tuple> CodeMappings { - get; - private set; - } } } diff --git a/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs b/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs index 351c28428..fafe13e1a 100644 --- a/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs +++ b/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs @@ -98,10 +98,27 @@ namespace ICSharpCode.Decompiler.ILAst } } - // Remove redundant return + // Remove redundant return at the end of method if (method.Body.Count > 0 && method.Body.Last().Match(ILCode.Ret) && ((ILExpression)method.Body.Last()).Arguments.Count == 0) { method.Body.RemoveAt(method.Body.Count - 1); } + + // Remove unreachable return statements + bool modified = false; + foreach(ILBlock block in method.GetSelfAndChildrenRecursive()) { + for (int i = 0; i < block.Body.Count - 1;) { + if (block.Body[i].IsUnconditionalControlFlow() && block.Body[i+1].Match(ILCode.Ret)) { + modified = true; + block.Body.RemoveAt(i+1); + } else { + i++; + } + } + } + if (modified) { + // More removals might be possible + new GotoRemoval().RemoveGotos(method); + } } IEnumerable GetParents(ILNode node) diff --git a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs index f6edfad85..e569c83e8 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs @@ -281,6 +281,8 @@ namespace ICSharpCode.Decompiler.ILAst int varCount = methodDef.Body.Variables.Count; + var exceptionHandlerStarts = new HashSet(methodDef.Body.ExceptionHandlers.Select(eh => instrToByteCode[eh.HandlerStart])); + // Add known states if(methodDef.Body.HasExceptionHandlers) { foreach(ExceptionHandler ex in methodDef.Body.ExceptionHandlers) { @@ -347,7 +349,12 @@ namespace ICSharpCode.Decompiler.ILAst // Find all successors List branchTargets = new List(); if (!byteCode.Code.IsUnconditionalControlFlow()) { - branchTargets.Add(byteCode.Next); + if (exceptionHandlerStarts.Contains(byteCode.Next)) { + // Do not fall though down to exception handler + // It is invalid IL as per ECMA-335 §12.4.2.8.1, but some obfuscators produce it + } else { + branchTargets.Add(byteCode.Next); + } } if (byteCode.Operand is Instruction[]) { foreach(Instruction inst in (Instruction[])byteCode.Operand) { diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index 0affa19aa..0543d10b0 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -58,6 +58,7 @@ namespace ICSharpCode.Decompiler.ILAst RemoveRedundantCode2, GotoRemoval, DuplicateReturns, + GotoRemoval2, ReduceIfNesting, InlineVariables3, CachedDelegateInitialization, @@ -190,6 +191,9 @@ namespace ICSharpCode.Decompiler.ILAst if (abortBeforeStep == ILAstOptimizationStep.DuplicateReturns) return; DuplicateReturnStatements(method); + if (abortBeforeStep == ILAstOptimizationStep.GotoRemoval2) return; + new GotoRemoval().RemoveGotos(method); + if (abortBeforeStep == ILAstOptimizationStep.ReduceIfNesting) return; ReduceIfNesting(method); diff --git a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs index 016f79916..c1cca6adc 100644 --- a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs +++ b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs @@ -864,7 +864,7 @@ namespace ICSharpCode.Decompiler.ILAst return null; } - static TypeReference UnpackModifiers(TypeReference type) + internal static TypeReference UnpackModifiers(TypeReference type) { while (type is OptionalModifierType || type is RequiredModifierType) type = ((TypeSpecification)type).ElementType; diff --git a/ICSharpCode.Decompiler/ITextOutput.cs b/ICSharpCode.Decompiler/ITextOutput.cs index 1cced95f2..608a45c64 100644 --- a/ICSharpCode.Decompiler/ITextOutput.cs +++ b/ICSharpCode.Decompiler/ITextOutput.cs @@ -24,6 +24,7 @@ namespace ICSharpCode.Decompiler public interface ITextOutput { int CurrentLine { get; } + int CurrentColumn { get; } void Indent(); void Unindent(); diff --git a/ICSharpCode.Decompiler/PlainTextOutput.cs b/ICSharpCode.Decompiler/PlainTextOutput.cs index 3e167b01b..86a28a5c8 100644 --- a/ICSharpCode.Decompiler/PlainTextOutput.cs +++ b/ICSharpCode.Decompiler/PlainTextOutput.cs @@ -23,25 +23,33 @@ namespace ICSharpCode.Decompiler { public sealed class PlainTextOutput : ITextOutput { + const int TAB_SIZE = 4; + readonly TextWriter writer; int indent; bool needsIndent; + int lineNumber = 1; + int columnNumber = 1; public PlainTextOutput(TextWriter writer) { if (writer == null) throw new ArgumentNullException("writer"); this.writer = writer; - CurrentLine = 1; } public PlainTextOutput() { this.writer = new StringWriter(); - CurrentLine = 1; } - public int CurrentLine { get; set; } + public int CurrentLine { + get { return lineNumber; } + } + + public int CurrentColumn { + get { return columnNumber; } + } public override string ToString() { @@ -64,6 +72,7 @@ namespace ICSharpCode.Decompiler needsIndent = false; for (int i = 0; i < indent; i++) { writer.Write('\t'); + columnNumber += TAB_SIZE - 1; } } } @@ -72,19 +81,22 @@ namespace ICSharpCode.Decompiler { WriteIndent(); writer.Write(ch); + columnNumber++; } public void Write(string text) { WriteIndent(); writer.Write(text); + columnNumber += text.Length; } public void WriteLine() { + lineNumber++; writer.WriteLine(); needsIndent = true; - ++CurrentLine; + columnNumber = TAB_SIZE * indent; } public void WriteDefinition(string text, object definition) diff --git a/ICSharpCode.Decompiler/Tests/Generics.cs b/ICSharpCode.Decompiler/Tests/Generics.cs index 9924583d6..9b7bd3fea 100644 --- a/ICSharpCode.Decompiler/Tests/Generics.cs +++ b/ICSharpCode.Decompiler/Tests/Generics.cs @@ -29,6 +29,12 @@ public static class Generics public Y Item2; } + public enum NestedEnum + { + A, + B + } + private T[] arr; public MyArray(int capacity) @@ -75,11 +81,13 @@ public static class Generics } } + private const Generics.MyArray.NestedEnum enumVal = Generics.MyArray.NestedEnum.A; private static Type type1 = typeof(List<>); private static Type type2 = typeof(Generics.MyArray<>); private static Type type3 = typeof(List<>.Enumerator); private static Type type4 = typeof(Generics.MyArray<>.NestedClass<>); private static Type type5 = typeof(List[]); + private static Type type6 = typeof(Generics.MyArray<>.NestedEnum); public static void MethodWithConstraint() where T : class, S where S : ICloneable, new() { diff --git a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj index ecf1a54e8..3aa9ea2a2 100644 --- a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj @@ -13,8 +13,8 @@ False 4 false - 67,169 False + 67,169,1058,728 x86 diff --git a/ICSharpCode.Decompiler/Tests/TestRunner.cs b/ICSharpCode.Decompiler/Tests/TestRunner.cs index 041a329b3..e5697ca07 100644 --- a/ICSharpCode.Decompiler/Tests/TestRunner.cs +++ b/ICSharpCode.Decompiler/Tests/TestRunner.cs @@ -164,7 +164,7 @@ namespace ICSharpCode.Decompiler.Tests { CSharpCodeProvider provider = new CSharpCodeProvider(new Dictionary { { "CompilerVersion", "v4.0" } }); CompilerParameters options = new CompilerParameters(); - options.CompilerOptions = "/unsafe"; + options.CompilerOptions = "/unsafe /o-"; options.ReferencedAssemblies.Add("System.Core.dll"); CompilerResults results = provider.CompileAssemblyFromSource(options, code); try { diff --git a/ILSpy.BamlDecompiler/BamlResourceEntryNode.cs b/ILSpy.BamlDecompiler/BamlResourceEntryNode.cs index cce7182f9..b79492d1f 100644 --- a/ILSpy.BamlDecompiler/BamlResourceEntryNode.cs +++ b/ILSpy.BamlDecompiler/BamlResourceEntryNode.cs @@ -3,14 +3,17 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Threading.Tasks; +using System.Xml; using System.Xml.Linq; - using ICSharpCode.AvalonEdit.Highlighting; +using ICSharpCode.ILSpy; using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.TreeNodes; +using Mono.Cecil; using Ricciolo.StylesExplorer.MarkupReflection; namespace ILSpy.BamlDecompiler @@ -45,17 +48,55 @@ namespace ILSpy.BamlDecompiler bool LoadBaml(AvalonEditTextOutput output) { var asm = this.Ancestors().OfType().FirstOrDefault().LoadedAssembly; - MemoryStream bamlStream = new MemoryStream(); Data.Position = 0; - Data.CopyTo(bamlStream); - bamlStream.Position = 0; - + XDocument xamlDocument = LoadIntoDocument(asm.GetAssemblyResolver(), asm.AssemblyDefinition, Data); + output.Write(xamlDocument.ToString()); + return true; + } + + internal static XDocument LoadIntoDocument(IAssemblyResolver resolver, AssemblyDefinition asm, Stream stream) + { XDocument xamlDocument; - using (XmlBamlReader reader = new XmlBamlReader(bamlStream, new CecilTypeResolver(asm))) + using (XmlBamlReader reader = new XmlBamlReader(stream, new CecilTypeResolver(resolver, asm))) xamlDocument = XDocument.Load(reader); + ConvertToEmptyElements(xamlDocument.Root); + MoveNamespacesToRoot(xamlDocument); + return xamlDocument; + } + + static void MoveNamespacesToRoot(XDocument xamlDocument) + { + var additionalXmlns = new List { + new XAttribute("xmlns", XmlBamlReader.DefaultWPFNamespace), + new XAttribute(XName.Get("x", XNamespace.Xmlns.NamespaceName), XmlBamlReader.XWPFNamespace) + }; - output.Write(xamlDocument.ToString()); - return true; + foreach (var element in xamlDocument.Root.DescendantsAndSelf()) { + if (element.Name.NamespaceName != XmlBamlReader.DefaultWPFNamespace && !additionalXmlns.Any(ka => ka.Value == element.Name.NamespaceName)) { + string newPrefix = new string(element.Name.LocalName.Where(c => char.IsUpper(c)).ToArray()).ToLowerInvariant(); + int current = additionalXmlns.Count(ka => ka.Name.Namespace == XNamespace.Xmlns && ka.Name.LocalName.TrimEnd(ch => char.IsNumber(ch)) == newPrefix); + if (current > 0) + newPrefix += (current + 1).ToString(); + XName defaultXmlns = XName.Get(newPrefix, XNamespace.Xmlns.NamespaceName); + if (element.Name.NamespaceName != XmlBamlReader.DefaultWPFNamespace) + additionalXmlns.Add(new XAttribute(defaultXmlns, element.Name.NamespaceName)); + } + } + + foreach (var xmlns in additionalXmlns.Except(xamlDocument.Root.Attributes())) { + xamlDocument.Root.Add(xmlns); + } + } + + static void ConvertToEmptyElements(XElement element) + { + foreach (var el in element.Elements()) { + if (!el.IsEmpty && !el.HasElements && el.Value == "") { + el.RemoveNodes(); + continue; + } + ConvertToEmptyElements(el); + } } } } \ No newline at end of file diff --git a/ILSpy.BamlDecompiler/CecilType.cs b/ILSpy.BamlDecompiler/CecilType.cs index 41a0fc203..e84f95938 100644 --- a/ILSpy.BamlDecompiler/CecilType.cs +++ b/ILSpy.BamlDecompiler/CecilType.cs @@ -67,5 +67,15 @@ namespace ILSpy.BamlDecompiler { return string.Format("[CecilType Type={0}]", type); } + + public IType BaseType { + get { + TypeDefinition td = type.BaseType.Resolve(); + if (td == null) + throw new Exception("could not resolve '" + type.BaseType.FullName + "'!"); + + return new CecilType(td); + } + } } } diff --git a/ILSpy.BamlDecompiler/CecilTypeResolver.cs b/ILSpy.BamlDecompiler/CecilTypeResolver.cs index 853025b06..702c37c38 100644 --- a/ILSpy.BamlDecompiler/CecilTypeResolver.cs +++ b/ILSpy.BamlDecompiler/CecilTypeResolver.cs @@ -14,11 +14,18 @@ namespace ILSpy.BamlDecompiler /// public class CecilTypeResolver : ITypeResolver { - LoadedAssembly assembly; + IAssemblyResolver resolver; + AssemblyDefinition thisAssembly; - public CecilTypeResolver(LoadedAssembly assembly) + public CecilTypeResolver(IAssemblyResolver resolver, AssemblyDefinition asm) { - this.assembly = assembly; + this.resolver = resolver; + this.thisAssembly = asm; + } + + public bool IsLocalAssembly(string name) + { + return name == this.thisAssembly.Name.Name; } public IType GetTypeByAssemblyQualifiedName(string name) @@ -31,12 +38,12 @@ namespace ILSpy.BamlDecompiler string fullName = name.Substring(0, comma); string assemblyName = name.Substring(comma + 1).Trim(); - var type = assembly.AssemblyDefinition.MainModule.GetType(fullName); + var type = thisAssembly.MainModule.GetType(fullName); if (type == null) { - var otherAssembly = assembly.LookupReferencedAssembly(assemblyName); + var otherAssembly = resolver.Resolve(assemblyName); if (otherAssembly == null) throw new Exception("could not resolve '" + assemblyName + "'!"); - type = otherAssembly.AssemblyDefinition.MainModule.GetType(fullName); + type = otherAssembly.MainModule.GetType(fullName); } return new CecilType(type); @@ -49,5 +56,11 @@ namespace ILSpy.BamlDecompiler return new CecilDependencyPropertyDescriptor(name, ((CecilType)ownerType).type); } + + public string RuntimeVersion { + get { + return thisAssembly.MainModule.Runtime.ToString(); + } + } } } diff --git a/ILSpy.BamlDecompiler/Extensions.cs b/ILSpy.BamlDecompiler/Extensions.cs new file mode 100644 index 000000000..c76a581ea --- /dev/null +++ b/ILSpy.BamlDecompiler/Extensions.cs @@ -0,0 +1,22 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team +// This code is distributed under the MS-PL (for details please see \doc\MS-PL.txt) + +using System; +using System.Linq; + +namespace ILSpy.BamlDecompiler +{ + public static class Extensions + { + public static string TrimEnd(this string target, Func predicate) + { + if (target == null) + throw new ArgumentNullException("target"); + + while (predicate(target.LastOrDefault())) + target = target.Remove(target.Length - 1); + + return target; + } + } +} diff --git a/ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj b/ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj index 6514c632b..a681f07fd 100644 --- a/ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj +++ b/ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj @@ -77,6 +77,7 @@ + @@ -97,6 +98,7 @@ + diff --git a/ILSpy.BamlDecompiler/Properties/AssemblyInfo.cs b/ILSpy.BamlDecompiler/Properties/AssemblyInfo.cs index f28440c93..a2c459d27 100644 --- a/ILSpy.BamlDecompiler/Properties/AssemblyInfo.cs +++ b/ILSpy.BamlDecompiler/Properties/AssemblyInfo.cs @@ -3,6 +3,7 @@ using System; using System.Reflection; using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; #endregion @@ -22,6 +23,8 @@ using System.Runtime.InteropServices; // If you need to expose a type to COM, use [ComVisible(true)] on that type. [assembly: ComVisible(false)] +[assembly: InternalsVisibleTo("ILSpy.BamlDecompiler.Tests")] + // The assembly version has following format : // // Major.Minor.Build.Revision diff --git a/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/AppDomainTypeResolver.cs b/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/AppDomainTypeResolver.cs index 9cc329459..b33379323 100644 --- a/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/AppDomainTypeResolver.cs +++ b/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/AppDomainTypeResolver.cs @@ -131,6 +131,17 @@ namespace Ricciolo.StylesExplorer.MarkupReflection return null; } } + + public bool IsLocalAssembly(string name) + { + return false; + } + + public string RuntimeVersion { + get { + throw new NotImplementedException(); + } + } #endregion diff --git a/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/DotNetType.cs b/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/DotNetType.cs index 1dbf1b21a..313863bd0 100644 --- a/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/DotNetType.cs +++ b/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/DotNetType.cs @@ -42,6 +42,12 @@ namespace Ricciolo.StylesExplorer.MarkupReflection if (_type == null) return false; return this._type.Equals(((DotNetType)type).Type); } + + public IType BaseType { + get { + return new DotNetType(this._type.BaseType.AssemblyQualifiedName); + } + } #endregion diff --git a/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/IType.cs b/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/IType.cs index 7ec8c79e4..b420bf480 100644 --- a/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/IType.cs +++ b/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/IType.cs @@ -8,10 +8,11 @@ using System.Text; namespace Ricciolo.StylesExplorer.MarkupReflection { /// - /// Interface rappresenting a DotNet type + /// Interface representing a DotNet type /// public interface IType { + IType BaseType { get; } string AssemblyQualifiedName { get; } bool IsSubclassOf(IType type); bool Equals(IType type); diff --git a/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/ITypeResolver.cs b/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/ITypeResolver.cs index 71e802a61..993126b32 100644 --- a/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/ITypeResolver.cs +++ b/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/ITypeResolver.cs @@ -9,6 +9,8 @@ namespace Ricciolo.StylesExplorer.MarkupReflection { public interface ITypeResolver { + string RuntimeVersion { get; } + bool IsLocalAssembly(string name); IType GetTypeByAssemblyQualifiedName(string name); IDependencyPropertyDescriptor GetDependencyPropertyDescriptor(string name, IType ownerType, IType targetType); } diff --git a/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/KnownInfo.cs b/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/KnownInfo.cs index e58762f41..81d7bea20 100644 --- a/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/KnownInfo.cs +++ b/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/KnownInfo.cs @@ -34,12 +34,16 @@ namespace Ricciolo.StylesExplorer.MarkupReflection public KnownInfo(ITypeResolver resolver) { - KnownAssemblyTable = new string[5]; - KnownAssemblyTable[0] = "PresentationFramework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"; - KnownAssemblyTable[1] = "PresentationCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"; - KnownAssemblyTable[2] = "mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"; - KnownAssemblyTable[3] = "System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"; - KnownAssemblyTable[4] = "WindowBase, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"; + switch (resolver.RuntimeVersion) { + case "Net_2_0": + LoadKnownAssemblies30(); + break; + case "Net_4_0": + LoadKnownAssemblies40(); + break; + default: + throw new NotSupportedException(); + } KnownTypeTable = new TypeDeclaration[760]; KnownTypeTable[0] = new TypeDeclaration(resolver, string.Empty, string.Empty, 0); @@ -1305,6 +1309,26 @@ namespace Ricciolo.StylesExplorer.MarkupReflection KnownResourceTable.Add(0xa9, new ResourceName("SystemParameters.WorkArea")); } + void LoadKnownAssemblies30() + { + KnownAssemblyTable = new string[5]; + KnownAssemblyTable[0] = "PresentationFramework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"; + KnownAssemblyTable[1] = "PresentationCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"; + KnownAssemblyTable[2] = "mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"; + KnownAssemblyTable[3] = "System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"; + KnownAssemblyTable[4] = "WindowBase, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"; + } + + void LoadKnownAssemblies40() + { + KnownAssemblyTable = new string[5]; + KnownAssemblyTable[0] = "PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"; + KnownAssemblyTable[1] = "PresentationCore, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"; + KnownAssemblyTable[2] = "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"; + KnownAssemblyTable[3] = "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"; + KnownAssemblyTable[4] = "WindowBase, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"; + } + #endregion public bool IsKnownType(string type) diff --git a/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/TypeDeclaration.cs b/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/TypeDeclaration.cs index 2efaab843..2acc523f4 100644 --- a/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/TypeDeclaration.cs +++ b/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/TypeDeclaration.cs @@ -132,6 +132,11 @@ namespace Ricciolo.StylesExplorer.MarkupReflection else return false; } + + public override int GetHashCode() + { + return this.AssemblyId ^ this.Name.GetHashCode() ^ this.Namespace.GetHashCode(); + } } } diff --git a/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/XmlBamlReader.cs b/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/XmlBamlReader.cs index 93ed72c66..6417549f7 100644 --- a/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/XmlBamlReader.cs +++ b/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/XmlBamlReader.cs @@ -8,6 +8,7 @@ using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; +using System.Linq; using System.Reflection; using System.Text; using System.Xml; @@ -54,6 +55,9 @@ namespace Ricciolo.StylesExplorer.MarkupReflection private readonly TypeDeclaration XamlTypeDeclaration; private readonly XmlNameTable _nameTable = new NameTable(); private IDictionary _rootNamespaces; + + public const string XWPFNamespace = "http://schemas.microsoft.com/winfx/2006/xaml"; + public const string DefaultWPFNamespace = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"; #endregion @@ -159,7 +163,7 @@ namespace Ricciolo.StylesExplorer.MarkupReflection public override bool MoveToFirstAttribute() { intoAttribute = false; - if (nodes.Count > 0 && nodes.Peek() is XmlBamlProperty) + if (nodes.Count > 0 && (nodes.Peek() is XmlBamlProperty || nodes.Peek() is XmlBamlSimpleProperty)) { _currentNode = nodes.Dequeue(); return true; @@ -178,7 +182,7 @@ namespace Ricciolo.StylesExplorer.MarkupReflection public override bool MoveToNextAttribute() { intoAttribute = false; - if (nodes.Count > 0 && nodes.Peek() is XmlBamlProperty) + if (nodes.Count > 0 && (nodes.Peek() is XmlBamlProperty || nodes.Peek() is XmlBamlSimpleProperty)) { _currentNode = nodes.Dequeue(); return true; @@ -196,7 +200,7 @@ namespace Ricciolo.StylesExplorer.MarkupReflection /// public override bool MoveToElement() { - while (nodes.Peek() is XmlBamlProperty) + while (nodes.Peek() is XmlBamlProperty || nodes.Peek() is XmlBamlSimpleProperty) { nodes.Dequeue(); } @@ -284,13 +288,7 @@ namespace Ricciolo.StylesExplorer.MarkupReflection else currentType = (BamlRecordType)type; - if (currentType.ToString().EndsWith("End")) - Debug.Unindent(); - - Debug.WriteLine(currentType); - - if (currentType.ToString().StartsWith("Start")) - Debug.Indent(); +// Debug.WriteLine(currentType); } private bool SetNextNode() @@ -300,6 +298,7 @@ namespace Ricciolo.StylesExplorer.MarkupReflection _currentNode = nodes.Dequeue(); if ((_currentNode is XmlBamlProperty)) continue; + if ((_currentNode is XmlBamlSimpleProperty)) continue; if (this.NodeType == XmlNodeType.EndElement) { @@ -451,7 +450,6 @@ namespace Ricciolo.StylesExplorer.MarkupReflection break; default: throw new NotImplementedException("UnsupportedNode: " + currentType); - break; } } @@ -586,7 +584,10 @@ namespace Ricciolo.StylesExplorer.MarkupReflection String localName = String.Empty; XmlBamlNode node = this.CurrentNode; - if (node is XmlBamlProperty) + if (node is XmlBamlSimpleProperty) { + var simpleNode = (XmlBamlSimpleProperty)node; + localName = simpleNode.LocalName; + } else if (node is XmlBamlProperty) { PropertyDeclaration pd = ((XmlBamlProperty)node).PropertyDeclaration; localName = FormatPropertyDeclaration(pd, false, true, true); @@ -1057,9 +1058,6 @@ namespace Ricciolo.StylesExplorer.MarkupReflection private void ReadPropertyComplexEnd() { - if (!(elements.Peek() is XmlBamlPropertyElement)) - throw new InvalidCastException(); - XmlBamlPropertyElement propertyElement = (XmlBamlPropertyElement) elements.Peek(); CloseElement(); @@ -1191,10 +1189,22 @@ namespace Ricciolo.StylesExplorer.MarkupReflection } else element = new XmlBamlElement(); + + // the type is defined in the local assembly, i.e., the main assembly + // and this is the root element + TypeDeclaration oldDeclaration = null; + if (_resolver.IsLocalAssembly(declaration.Assembly) && parentElement == null) { + oldDeclaration = declaration; + declaration = GetKnownTypeDeclarationByName(declaration.Type.BaseType.AssemblyQualifiedName); + } element.TypeDeclaration = declaration; elements.Push(element); nodes.Enqueue(element); + + if (oldDeclaration != null) { + nodes.Enqueue(new XmlBamlSimpleProperty(XWPFNamespace, "Class", string.Format("{0}.{1}", oldDeclaration.Namespace, oldDeclaration.Name))); + } if (parentElement != null && complexPropertyOpened == 0) { @@ -1572,6 +1582,16 @@ namespace Ricciolo.StylesExplorer.MarkupReflection return declaration; } + + TypeDeclaration GetKnownTypeDeclarationByName(string name) + { + foreach (var type in KnownInfo.KnownTypeTable) { + if (name == string.Format("{0}.{1}, {2}", type.Namespace, type.Name, type.Assembly)) + return type; + } + + throw new NotSupportedException(); + } internal string GetAssembly(short identifier) { @@ -1602,7 +1622,9 @@ namespace Ricciolo.StylesExplorer.MarkupReflection TypeDeclaration declaration; XmlBamlNode node = this.CurrentNode; - if (node is XmlBamlProperty) + if (node is XmlBamlSimpleProperty) + return ((XmlBamlSimpleProperty)node).NamespaceName; + else if (node is XmlBamlProperty) { declaration = ((XmlBamlProperty)node).PropertyDeclaration.DeclaringType; TypeDeclaration elementDeclaration = this.readingElements.Peek().TypeDeclaration; @@ -1678,7 +1700,9 @@ namespace Ricciolo.StylesExplorer.MarkupReflection get { XmlBamlNode node = this.CurrentNode; - if (node is XmlBamlProperty) + if (node is XmlBamlSimpleProperty) + return ((XmlBamlSimpleProperty)node).Value; + else if (node is XmlBamlProperty) return ((XmlBamlProperty)node).Value.ToString(); else if (node is XmlBamlText) return ((XmlBamlText)node).Text; diff --git a/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/XmlBamlSimpleProperty.cs b/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/XmlBamlSimpleProperty.cs new file mode 100644 index 000000000..be00e41e1 --- /dev/null +++ b/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/XmlBamlSimpleProperty.cs @@ -0,0 +1,34 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team +// This code is distributed under the MS-PL (for details please see \doc\MS-PL.txt) + +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; + +namespace Ricciolo.StylesExplorer.MarkupReflection +{ + class XmlBamlSimpleProperty : XmlBamlNode + { + public string NamespaceName { get; private set; } + public string LocalName { get; private set; } + public string Value { get; private set; } + + public XmlBamlSimpleProperty(string namespaceName, string localName, string value) + { + if (string.IsNullOrWhiteSpace(namespaceName)) + throw new ArgumentException("namespaceName"); + if (string.IsNullOrWhiteSpace(localName)) + throw new ArgumentException("localName"); + if (value == null) + throw new ArgumentNullException("value"); + this.NamespaceName = namespaceName; + this.LocalName = localName; + this.Value = value; + } + + public override XmlNodeType NodeType { + get { return XmlNodeType.Attribute; } + } + } +} diff --git a/ILSpy.BamlDecompiler/Tests/Cases/Simple.xaml b/ILSpy.BamlDecompiler/Tests/Cases/Simple.xaml new file mode 100644 index 000000000..ab5d5afc6 --- /dev/null +++ b/ILSpy.BamlDecompiler/Tests/Cases/Simple.xaml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/ILSpy.BamlDecompiler/Tests/Cases/Simple.xaml.cs b/ILSpy.BamlDecompiler/Tests/Cases/Simple.xaml.cs new file mode 100644 index 000000000..12770ff11 --- /dev/null +++ b/ILSpy.BamlDecompiler/Tests/Cases/Simple.xaml.cs @@ -0,0 +1,26 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; + +namespace ILSpy.BamlDecompiler.Tests.Cases +{ + /// + /// Interaction logic for Simple.xaml + /// + public partial class Simple : Window + { + public Simple() + { + InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/ILSpy.BamlDecompiler/Tests/Cases/SimpleDictionary.xaml b/ILSpy.BamlDecompiler/Tests/Cases/SimpleDictionary.xaml new file mode 100644 index 000000000..4eb8fb3c7 --- /dev/null +++ b/ILSpy.BamlDecompiler/Tests/Cases/SimpleDictionary.xaml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ILSpy.BamlDecompiler/Tests/ILSpy.BamlDecompiler.Tests.csproj b/ILSpy.BamlDecompiler/Tests/ILSpy.BamlDecompiler.Tests.csproj new file mode 100644 index 000000000..61cfa995c --- /dev/null +++ b/ILSpy.BamlDecompiler/Tests/ILSpy.BamlDecompiler.Tests.csproj @@ -0,0 +1,109 @@ + + + + {1169E6D1-1899-43D4-A500-07CE4235B388} + Debug + x86 + Library + ILSpy.BamlDecompiler.Tests + ILSpy.BamlDecompiler.Tests + v4.0 + Client + False + False + 4 + false + + + x86 + False + Auto + 4194304 + 4096 + + + ..\bin\Debug\ + true + Full + False + True + DEBUG;TRACE + + + ..\bin\Release\ + false + None + True + False + TRACE + + + + ..\..\packages\DiffLib.1.0.0.55\lib\net35-Client\DiffLib.dll + + + ..\..\ICSharpCode.Decompiler\Tests\nunit.framework.dll + + + 3.0 + + + 3.0 + + + + 3.5 + + + 4.0 + + + + 3.5 + + + 3.0 + + + + + Simple.xaml + Code + + + + + + + {FEC0DA52-C4A6-4710-BE36-B484A20C5E22} + ICSharpCode.Decompiler.Tests + + + {1E85EFF9-E370-4683-83E4-8A3D063FF791} + ILSpy + + + {D68133BD-1E63-496E-9EDE-4FBDBF77B486} + Mono.Cecil + + + {DDE2A481-8271-4EAC-A330-8FA6A38D13D1} + ICSharpCode.TreeView + + + {A6BAD2BA-76BA-461C-8B6D-418607591247} + ILSpy.BamlDecompiler + + + + + + + + + Always + + + + + \ No newline at end of file diff --git a/ILSpy.BamlDecompiler/Tests/Properties/AssemblyInfo.cs b/ILSpy.BamlDecompiler/Tests/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..30c5daa49 --- /dev/null +++ b/ILSpy.BamlDecompiler/Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,31 @@ +#region Using directives + +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ILSpy.BamlDecompiler.Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ILSpy.BamlDecompiler.Tests")] +[assembly: AssemblyCopyright("Copyright 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.*")] diff --git a/ILSpy.BamlDecompiler/Tests/TestRunner.cs b/ILSpy.BamlDecompiler/Tests/TestRunner.cs new file mode 100644 index 000000000..441116873 --- /dev/null +++ b/ILSpy.BamlDecompiler/Tests/TestRunner.cs @@ -0,0 +1,73 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Resources; +using System.Xml.Linq; +using ICSharpCode.Decompiler.Tests.Helpers; +using ICSharpCode.ILSpy; +using Mono.Cecil; +using NUnit.Framework; +using Ricciolo.StylesExplorer.MarkupReflection; + +namespace ILSpy.BamlDecompiler.Tests +{ + [TestFixture] + public class TestRunner + { + [Test] + public void Simple() + { + RunTest("cases/simple"); + } + + [Test] + public void SimpleDictionary() + { + RunTest("cases/simpledictionary"); + } + + void RunTest(string name) + { + string asmPath = typeof(TestRunner).Assembly.Location; + var assembly = AssemblyDefinition.ReadAssembly(asmPath); + Resource res = assembly.MainModule.Resources.First(); + Stream bamlStream = LoadBaml(res, name + ".baml"); + Assert.IsNotNull(bamlStream); + XDocument document = BamlResourceEntryNode.LoadIntoDocument(new DefaultAssemblyResolver(), assembly, bamlStream); + string path = Path.Combine("..\\..\\Tests", name + ".xaml"); + + CodeAssert.AreEqual(document.ToString(), File.ReadAllText(path)); + } + + Stream LoadBaml(Resource res, string name) + { + EmbeddedResource er = res as EmbeddedResource; + if (er != null) { + Stream s = er.GetResourceStream(); + s.Position = 0; + ResourceReader reader; + try { + reader = new ResourceReader(s); + } + catch (ArgumentException) { + return null; + } + foreach (DictionaryEntry entry in reader.Cast().OrderBy(e => e.Key.ToString())) { + if (entry.Key.ToString() == name) { + if (entry.Value is Stream) + return (Stream)entry.Value; + if (entry.Value is byte[]) + return new MemoryStream((byte[])entry.Value); + } + } + } + + return null; + } + } +} diff --git a/ILSpy.sln b/ILSpy.sln index 8d9ca1f01..acf31153d 100644 --- a/ILSpy.sln +++ b/ILSpy.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 -# SharpDevelop 4.0.1.7146 +# SharpDevelop 4.1.0.7466-alpha Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "doc", "doc", "{F45DB999-7E72-4000-B5AD-3A7B485A0896}" ProjectSection(SolutionItems) = postProject doc\Command Line.txt = doc\Command Line.txt @@ -27,6 +27,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Cecil.Pdb", "Mono.Ceci EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILSpy.BamlDecompiler", "ILSpy.BamlDecompiler\ILSpy.BamlDecompiler.csproj", "{A6BAD2BA-76BA-461C-8B6D-418607591247}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILSpy.BamlDecompiler.Tests", "ILSpy.BamlDecompiler\Tests\ILSpy.BamlDecompiler.Tests.csproj", "{1169E6D1-1899-43D4-A500-07CE4235B388}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -115,6 +117,14 @@ Global {A6BAD2BA-76BA-461C-8B6D-418607591247}.Release|Any CPU.ActiveCfg = Release|Any CPU {A6BAD2BA-76BA-461C-8B6D-418607591247}.Release|x86.Build.0 = Release|x86 {A6BAD2BA-76BA-461C-8B6D-418607591247}.Release|x86.ActiveCfg = Release|x86 + {1169E6D1-1899-43D4-A500-07CE4235B388}.Debug|Any CPU.Build.0 = Debug|x86 + {1169E6D1-1899-43D4-A500-07CE4235B388}.Debug|Any CPU.ActiveCfg = Debug|x86 + {1169E6D1-1899-43D4-A500-07CE4235B388}.Debug|x86.Build.0 = Debug|x86 + {1169E6D1-1899-43D4-A500-07CE4235B388}.Debug|x86.ActiveCfg = Debug|x86 + {1169E6D1-1899-43D4-A500-07CE4235B388}.Release|Any CPU.Build.0 = Release|x86 + {1169E6D1-1899-43D4-A500-07CE4235B388}.Release|Any CPU.ActiveCfg = Release|x86 + {1169E6D1-1899-43D4-A500-07CE4235B388}.Release|x86.Build.0 = Release|x86 + {1169E6D1-1899-43D4-A500-07CE4235B388}.Release|x86.ActiveCfg = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/ILSpy/AboutPage.cs b/ILSpy/AboutPage.cs index fd4fb4891..29e5ac262 100644 --- a/ILSpy/AboutPage.cs +++ b/ILSpy/AboutPage.cs @@ -31,8 +31,10 @@ using System.Windows.Data; using System.Windows.Input; using System.Xml; using System.Xml.Linq; + using ICSharpCode.AvalonEdit.Rendering; using ICSharpCode.Decompiler; +using ICSharpCode.ILSpy.Bookmarks; using ICSharpCode.ILSpy.TextView; namespace ICSharpCode.ILSpy @@ -94,6 +96,9 @@ namespace ICSharpCode.ILSpy output.AddVisualLineElementGenerator(new MyLinkElementGenerator("MIT License", "resource:license.txt")); output.AddVisualLineElementGenerator(new MyLinkElementGenerator("LGPL", "resource:LGPL.txt")); textView.ShowText(output); + + //reset icon bar + textView.manager.Bookmarks.Clear(); } sealed class MyLinkElementGenerator : LinkElementGenerator diff --git a/ILSpy/AnalyzerTreeView.cs b/ILSpy/AnalyzerTreeView.cs index d89174d18..94f4ed66c 100644 --- a/ILSpy/AnalyzerTreeView.cs +++ b/ILSpy/AnalyzerTreeView.cs @@ -17,15 +17,6 @@ // DEALINGS IN THE SOFTWARE. using System; -using System.Collections.Generic; -using System.Text; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; - using ICSharpCode.ILSpy.TreeNodes.Analyzer; using ICSharpCode.TreeView; @@ -37,9 +28,11 @@ namespace ICSharpCode.ILSpy public partial class AnalyzerTreeView : SharpTreeView, IPane { static AnalyzerTreeView instance; - - public static AnalyzerTreeView Instance { - get { + + public static AnalyzerTreeView Instance + { + get + { if (instance == null) { App.Current.VerifyAccess(); instance = new AnalyzerTreeView(); @@ -47,42 +40,33 @@ namespace ICSharpCode.ILSpy return instance; } } - + private AnalyzerTreeView() { this.ShowRoot = false; this.Root = new AnalyzerTreeNode { Language = MainWindow.Instance.CurrentLanguage }; ContextMenuProvider.Add(this); } - + public void Show() { if (!IsVisible) MainWindow.Instance.ShowInBottomPane("Analyzer", this); } - + public void Show(AnalyzerTreeNode node) { Show(); - + node.IsExpanded = true; this.Root.Children.Add(node); this.SelectedItem = node; this.FocusNode(node); } - + void IPane.Closed() { this.Root.Children.Clear(); } } - - [ExportMainMenuCommand(Menu = "_View", Header = "_Analyzer", MenuCategory = "ShowPane", MenuOrder = 100)] - sealed class ShowAnalyzerCommand : SimpleCommand - { - public override void Execute(object parameter) - { - AnalyzerTreeView.Instance.Show(); - } - } } \ No newline at end of file diff --git a/ILSpy/AvalonEdit/ITextMarker.cs b/ILSpy/AvalonEdit/ITextMarker.cs new file mode 100644 index 000000000..8046bb6a9 --- /dev/null +++ b/ILSpy/AvalonEdit/ITextMarker.cs @@ -0,0 +1,123 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Windows.Media; + +using ICSharpCode.ILSpy.Bookmarks; + +namespace ICSharpCode.ILSpy.AvalonEdit +{ + /// + /// Represents a text marker. + /// + public interface ITextMarker + { + /// + /// Gets the start offset of the marked text region. + /// + int StartOffset { get; } + + /// + /// Gets the end offset of the marked text region. + /// + int EndOffset { get; } + + /// + /// Gets the length of the marked region. + /// + int Length { get; } + + /// + /// Deletes the text marker. + /// + void Delete(); + + /// + /// Gets whether the text marker was deleted. + /// + bool IsDeleted { get; } + + /// + /// Event that occurs when the text marker is deleted. + /// + event EventHandler Deleted; + + /// + /// Gets/Sets the background color. + /// + Color? BackgroundColor { get; set; } + + /// + /// Gets/Sets the foreground color. + /// + Color? ForegroundColor { get; set; } + + /// + /// Gets/Sets the type of the marker. Use TextMarkerType.None for normal markers. + /// + TextMarkerType MarkerType { get; set; } + + /// + /// Gets/Sets the color of the marker. + /// + Color MarkerColor { get; set; } + + /// + /// Gets/Sets an object with additional data for this text marker. + /// + object Tag { get; set; } + + /// + /// Gets/Sets an object that will be displayed as tooltip in the text editor. + /// + object ToolTip { get; set; } + + /// + /// Gets or sets if the marker is visible or not. + /// + Predicate IsVisible { get; set; } + + /// + /// Gets or sets the bookmark. + /// + IBookmark Bookmark { get; set; } + } + + public enum TextMarkerType + { + /// + /// Use no marker + /// + None, + /// + /// Use squiggly underline marker + /// + SquigglyUnderline + } + + public interface ITextMarkerService + { + /// + /// Creates a new text marker. The text marker will be invisible at first, + /// you need to set one of the Color properties to make it visible. + /// + ITextMarker Create(int startOffset, int length); + + /// + /// Gets the list of text markers. + /// + IEnumerable TextMarkers { get; } + + /// + /// Removes the specified text marker. + /// + void Remove(ITextMarker marker); + + /// + /// Removes all text markers that match the condition. + /// + void RemoveAll(Predicate predicate); + } +} diff --git a/ILSpy/AvalonEdit/IconBarManager.cs b/ILSpy/AvalonEdit/IconBarManager.cs new file mode 100644 index 000000000..3677df10b --- /dev/null +++ b/ILSpy/AvalonEdit/IconBarManager.cs @@ -0,0 +1,83 @@ +// Copyright (c) 2011 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.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Linq; + +using ICSharpCode.ILSpy.Bookmarks; +using ICSharpCode.NRefactory.CSharp; + +namespace ICSharpCode.ILSpy.AvalonEdit +{ + /// + /// Stores the entries in the icon bar margin. Multiple icon bar margins + /// can use the same manager if split view is used. + /// + public class IconBarManager : IBookmarkMargin + { + ObservableCollection bookmarks = new ObservableCollection(); + + public IconBarManager() + { + bookmarks.CollectionChanged += bookmarks_CollectionChanged; + } + + public IList Bookmarks { + get { return bookmarks; } + } + + void bookmarks_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + Redraw(); + } + + public void Redraw() + { + if (RedrawRequested != null) + RedrawRequested(this, EventArgs.Empty); + } + + public event EventHandler RedrawRequested; + + internal void UpdateClassMemberBookmarks(IEnumerable nodes) + { + this.bookmarks.Clear(); + + if (nodes == null || nodes.Count() == 0) + return; + + foreach (var n in nodes) { + switch (n.NodeType) { + case NodeType.TypeDeclaration: + case NodeType.TypeReference: + this.bookmarks.Add(new TypeBookmark(n)); + break; + case NodeType.Member: + this.bookmarks.Add(new MemberBookmark(n)); + break; + default: + // do nothing + break; + } + } + } + } +} diff --git a/ILSpy/AvalonEdit/IconBarMargin.cs b/ILSpy/AvalonEdit/IconBarMargin.cs new file mode 100644 index 000000000..36ac3817a --- /dev/null +++ b/ILSpy/AvalonEdit/IconBarMargin.cs @@ -0,0 +1,243 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Diagnostics; +using System.Linq; +using System.Windows; +using System.Windows.Input; +using System.Windows.Media; + +using ICSharpCode.AvalonEdit.Editing; +using ICSharpCode.AvalonEdit.Rendering; +using ICSharpCode.AvalonEdit.Utils; +using ICSharpCode.Decompiler; +using ICSharpCode.ILSpy.Bookmarks; +using ICSharpCode.NRefactory.CSharp; +using Mono.Cecil; + +namespace ICSharpCode.ILSpy.AvalonEdit +{ + public class IconBarMargin : AbstractMargin, IDisposable + { + readonly IconBarManager manager; + + public IconBarMargin(IconBarManager manager) + { + BookmarkManager.Added += delegate { InvalidateVisual(); }; + BookmarkManager.Removed += delegate { InvalidateVisual(); }; + + this.manager = manager; + } + + public IconBarManager Manager { + get { return manager; } + } + + public IList DecompiledMembers { get; set; } + + public virtual void Dispose() + { + this.TextView = null; // detach from TextView (will also detach from manager) + } + + /// + protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters) + { + // accept clicks even when clicking on the background + return new PointHitTestResult(this, hitTestParameters.HitPoint); + } + + /// + protected override Size MeasureOverride(Size availableSize) + { + return new Size(18, 0); + } + + protected override void OnRender(DrawingContext drawingContext) + { + Size renderSize = this.RenderSize; + drawingContext.DrawRectangle(SystemColors.ControlBrush, null, + new Rect(0, 0, renderSize.Width, renderSize.Height)); + drawingContext.DrawLine(new Pen(SystemColors.ControlDarkBrush, 1), + new Point(renderSize.Width - 0.5, 0), + new Point(renderSize.Width - 0.5, renderSize.Height)); + + ICSharpCode.AvalonEdit.Rendering.TextView textView = this.TextView; + if (textView != null && textView.VisualLinesValid) { + // create a dictionary line number => first bookmark + Dictionary bookmarkDict = new Dictionary(); + foreach (var bm in BookmarkManager.Bookmarks) { + if (!DecompiledMembers.Contains(bm.MemberReference)) + continue; + int line = bm.LineNumber; + IBookmark existingBookmark; + if (!bookmarkDict.TryGetValue(line, out existingBookmark) || bm.ZOrder > existingBookmark.ZOrder) + bookmarkDict[line] = bm; + } + + foreach (var bm in manager.Bookmarks) { + int line = bm.LineNumber; + IBookmark existingBookmark; + if (!bookmarkDict.TryGetValue(line, out existingBookmark) || bm.ZOrder > existingBookmark.ZOrder) + bookmarkDict[line] = bm; + } + + Size pixelSize = PixelSnapHelpers.GetPixelSize(this); + foreach (VisualLine line in textView.VisualLines) { + int lineNumber = line.FirstDocumentLine.LineNumber; + IBookmark bm; + if (bookmarkDict.TryGetValue(lineNumber, out bm)) { + Rect rect = new Rect(0, PixelSnapHelpers.Round(line.VisualTop - textView.VerticalOffset, pixelSize.Height), 16, 16); + if (dragDropBookmark == bm && dragStarted) + drawingContext.PushOpacity(0.5); + drawingContext.DrawImage(bm.Image, rect); + if (dragDropBookmark == bm && dragStarted) + drawingContext.Pop(); + } + } + if (dragDropBookmark != null && dragStarted) { + Rect rect = new Rect(0, PixelSnapHelpers.Round(dragDropCurrentPoint - 8, pixelSize.Height), 16, 16); + drawingContext.DrawImage(dragDropBookmark.Image, rect); + } + } + } + + IBookmark dragDropBookmark; // bookmark being dragged (!=null if drag'n'drop is active) + double dragDropStartPoint; + double dragDropCurrentPoint; + bool dragStarted; // whether drag'n'drop operation has started (mouse was moved minimum distance) + + protected override void OnMouseDown(MouseButtonEventArgs e) + { + base.OnMouseDown(e); + int line = GetLineFromMousePosition(e); + if (!e.Handled && line > 0) { + IBookmark bm = GetBookmarkFromLine(line); + if (bm != null) { + bm.MouseDown(e); + if (!e.Handled) { + if (e.ChangedButton == MouseButton.Left && bm.CanDragDrop && CaptureMouse()) { + StartDragDrop(bm, e); + e.Handled = true; + } + } + } + } + // don't allow selecting text through the IconBarMargin + if (e.ChangedButton == MouseButton.Left) + e.Handled = true; + } + + IBookmark GetBookmarkFromLine(int line) + { + BookmarkBase result = null; + foreach (BookmarkBase bm in BookmarkManager.Bookmarks) { + if (bm.LineNumber == line && + this.DecompiledMembers != null && this.DecompiledMembers.Contains(bm.MemberReference)) { + if (result == null || bm.ZOrder > result.ZOrder) + return result; + } + } + + return manager.Bookmarks.FirstOrDefault(b => b.LineNumber == line); + } + + protected override void OnLostMouseCapture(MouseEventArgs e) + { + CancelDragDrop(); + base.OnLostMouseCapture(e); + } + + void StartDragDrop(IBookmark bm, MouseEventArgs e) + { + dragDropBookmark = bm; + dragDropStartPoint = dragDropCurrentPoint = e.GetPosition(this).Y; + if (TextView != null) { + TextArea area = TextView.Services.GetService(typeof(TextArea)) as TextArea; + if (area != null) + area.PreviewKeyDown += TextArea_PreviewKeyDown; + } + } + + void CancelDragDrop() + { + if (dragDropBookmark != null) { + dragDropBookmark = null; + dragStarted = false; + if (TextView != null) { + TextArea area = TextView.Services.GetService(typeof(TextArea)) as TextArea; + if (area != null) + area.PreviewKeyDown -= TextArea_PreviewKeyDown; + } + ReleaseMouseCapture(); + InvalidateVisual(); + } + } + + void TextArea_PreviewKeyDown(object sender, KeyEventArgs e) + { + // any key press cancels drag'n'drop + CancelDragDrop(); + if (e.Key == Key.Escape) + e.Handled = true; + } + + internal int GetLineFromMousePosition(MouseEventArgs e) + { + ICSharpCode.AvalonEdit.Rendering.TextView textView = this.TextView; + if (textView == null) + return 0; + VisualLine vl = textView.GetVisualLineFromVisualTop(e.GetPosition(textView).Y + textView.ScrollOffset.Y); + if (vl == null) + return 0; + return vl.FirstDocumentLine.LineNumber; + } + + protected override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + if (dragDropBookmark != null) { + dragDropCurrentPoint = e.GetPosition(this).Y; + if (Math.Abs(dragDropCurrentPoint - dragDropStartPoint) > SystemParameters.MinimumVerticalDragDistance) + dragStarted = true; + InvalidateVisual(); + } + } + + protected override void OnMouseUp(MouseButtonEventArgs e) + { + base.OnMouseUp(e); + int line = GetLineFromMousePosition(e); + if (!e.Handled && dragDropBookmark != null) { + if (dragStarted) { + if (line != 0) + dragDropBookmark.Drop(line); + e.Handled = true; + } + CancelDragDrop(); + } + if (!e.Handled && line != 0) { + var bm = GetBookmarkFromLine(line); + if (bm != null) { + bm.MouseUp(e); + + if (bm is BookmarkBase) { + if ((bm as BookmarkBase).CanToggle) { + BookmarkManager.RemoveMark(bm as BookmarkBase); + InvalidateVisual(); + } + } + if (e.Handled) + return; + } + if (e.ChangedButton == MouseButton.Left) { + // TODO: notify subscribers + } + InvalidateVisual(); + } + } + } +} diff --git a/ILSpy/AvalonEdit/IconMarginActionsProvider.cs b/ILSpy/AvalonEdit/IconMarginActionsProvider.cs new file mode 100644 index 000000000..3df691d33 --- /dev/null +++ b/ILSpy/AvalonEdit/IconMarginActionsProvider.cs @@ -0,0 +1,189 @@ +// Copyright (c) 2011 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.ComponentModel.Composition; +using System.Linq; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; + +using ICSharpCode.ILSpy.Bookmarks; + +namespace ICSharpCode.ILSpy.AvalonEdit +{ + #region Context menu extensibility + public interface IBookmarkContextMenuEntry + { + bool IsVisible(IBookmark[] bookmarks); + bool IsEnabled(IBookmark[] bookmarks); + void Execute(IBookmark[] bookmarks); + } + + public interface IBookmarkContextMenuEntryMetadata + { + string Icon { get; } + string Header { get; } + string Category { get; } + + double Order { get; } + } + + [MetadataAttribute] + [AttributeUsage(AttributeTargets.Class, AllowMultiple=false)] + public class ExportBookmarkContextMenuEntryAttribute : ExportAttribute, IBookmarkContextMenuEntryMetadata + { + public ExportBookmarkContextMenuEntryAttribute() + : base(typeof(IBookmarkContextMenuEntry)) + { + } + + public string Icon { get; set; } + public string Header { get; set; } + public string Category { get; set; } + public double Order { get; set; } + } + #endregion + + #region Actions (simple clicks) - this will be used for creating bookmarks (e.g. Breakpoint bookmarks) + + public interface IBookmarkActionEntry + { + bool IsEnabled(); + void Execute(); + } + + public interface IBookmarkActionMetadata + { + string Category { get; } + + double Order { get; } + } + + [MetadataAttribute] + [AttributeUsage(AttributeTargets.Class, AllowMultiple=false)] + public class ExportBookmarkActionEntryAttribute : ExportAttribute, IBookmarkActionMetadata + { + public ExportBookmarkActionEntryAttribute() + : base(typeof(IBookmarkActionEntry)) + { + } + + public string Icon { get; set; } + public string Header { get; set; } + public string Category { get; set; } + public double Order { get; set; } + } + + #endregion + + internal class IconMarginActionsProvider + { + /// + /// Enables extensible context menu support for the specified icon margin. + /// + public static void Add(IconBarMargin margin) + { + var provider = new IconMarginActionsProvider(margin); + margin.MouseDown += provider.MouseDown; + margin.ContextMenu = new ContextMenu(); + } + + readonly IconBarMargin margin; + + [ImportMany(typeof(IBookmarkContextMenuEntry))] + Lazy[] contextEntries = null; + + [ImportMany(typeof(IBookmarkActionEntry))] + Lazy[] actionEntries = null; + + private IconMarginActionsProvider(IconBarMargin margin) + { + this.margin = margin; + App.CompositionContainer.ComposeParts(this); + } + + void MouseDown(object sender, MouseButtonEventArgs e) + { + int line = margin.GetLineFromMousePosition(e); + + var bookmarks = margin.Manager.Bookmarks.ToArray(); + if (bookmarks.Length == 0) { + // don't show the menu + e.Handled = true; + this.margin.ContextMenu = null; + return; + } + + if (e.LeftButton == MouseButtonState.Pressed) { + foreach (var category in actionEntries.OrderBy(c => c.Metadata.Order).GroupBy(c => c.Metadata.Category)) { + foreach (var entryPair in category) { + IBookmarkActionEntry entry = entryPair.Value; + + if (entryPair.Value.IsEnabled()) { + entry.Execute(); + } + } + } + } + + if (e.RightButton == MouseButtonState.Pressed) { + // check if we are on a Member + var bookmark = bookmarks.FirstOrDefault(b => b.LineNumber == line); + if (bookmark == null) { + // don't show the menu + e.Handled = true; + this.margin.ContextMenu = null; + return; + } + + var marks = new[] { bookmark }; + ContextMenu menu = new ContextMenu(); + foreach (var category in contextEntries.OrderBy(c => c.Metadata.Order).GroupBy(c => c.Metadata.Category)) { + if (menu.Items.Count > 0) { + menu.Items.Add(new Separator()); + } + foreach (var entryPair in category) { + IBookmarkContextMenuEntry entry = entryPair.Value; + if (entry.IsVisible(marks)) { + MenuItem menuItem = new MenuItem(); + menuItem.Header = entryPair.Metadata.Header; + if (!string.IsNullOrEmpty(entryPair.Metadata.Icon)) { + menuItem.Icon = new Image { + Width = 16, + Height = 16, + Source = Images.LoadImage(entry, entryPair.Metadata.Icon) + }; + } + if (entryPair.Value.IsEnabled(marks)) { + menuItem.Click += delegate { entry.Execute(marks); }; + } else + menuItem.IsEnabled = false; + menu.Items.Add(menuItem); + } + } + } + if (menu.Items.Count > 0) + margin.ContextMenu = menu; + else + // hide the context menu. + e.Handled = true; + } + } + } +} diff --git a/ILSpy/AvalonEdit/TextEditorWeakEventManager.cs b/ILSpy/AvalonEdit/TextEditorWeakEventManager.cs new file mode 100644 index 000000000..2074763d8 --- /dev/null +++ b/ILSpy/AvalonEdit/TextEditorWeakEventManager.cs @@ -0,0 +1,67 @@ +// Copyright (c) 2011 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.Windows; +using ICSharpCode.AvalonEdit; +using ICSharpCode.AvalonEdit.Utils; + +namespace ICSharpCode.ILSpy.AvalonEdit +{ + public static class TextEditorWeakEventManager + { + public sealed class MouseHover : WeakEventManagerBase + { + protected override void StopListening(TextEditor source) + { + source.MouseHover -= DeliverEvent; + } + + protected override void StartListening(TextEditor source) + { + source.MouseHover += DeliverEvent; + } + } + + public sealed class MouseHoverStopped : WeakEventManagerBase + { + protected override void StopListening(TextEditor source) + { + source.MouseHoverStopped -= DeliverEvent; + } + + protected override void StartListening(TextEditor source) + { + source.MouseHoverStopped += DeliverEvent; + } + } + + public sealed class MouseDown : WeakEventManagerBase + { + protected override void StopListening(TextEditor source) + { + source.MouseDown -= DeliverEvent; + } + + protected override void StartListening(TextEditor source) + { + source.MouseDown += DeliverEvent; + } + } + } +} diff --git a/ILSpy/AvalonEdit/TextMarkerService.cs b/ILSpy/AvalonEdit/TextMarkerService.cs new file mode 100644 index 000000000..6b97b5650 --- /dev/null +++ b/ILSpy/AvalonEdit/TextMarkerService.cs @@ -0,0 +1,292 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Linq; +using System.Windows; +using System.Windows.Media; +using System.Windows.Threading; + +using ICSharpCode.AvalonEdit; +using ICSharpCode.AvalonEdit.Document; +using ICSharpCode.AvalonEdit.Rendering; +using ICSharpCode.ILSpy.Bookmarks; + +namespace ICSharpCode.ILSpy.AvalonEdit +{ + /// + /// Handles the text markers for a code editor. + /// + public sealed class TextMarkerService : DocumentColorizingTransformer, IBackgroundRenderer, ITextMarkerService + { + TextEditor codeEditor; + + TextSegmentCollection markers = new TextSegmentCollection(); + + public TextMarkerService() + { + } + + public TextEditor CodeEditor { + get { return codeEditor; } + set { codeEditor = value; } + } + + #region ITextMarkerService + public ITextMarker Create(int startOffset, int length) + { + int textLength = codeEditor.TextArea.TextView.Document.TextLength; + if (startOffset < 0 || startOffset > textLength) + throw new ArgumentOutOfRangeException("startOffset", startOffset, "Value must be between 0 and " + textLength); + if (length < 0 || startOffset + length > textLength) + throw new ArgumentOutOfRangeException("length", length, "length must not be negative and startOffset+length must not be after the end of the document"); + + TextMarker m = new TextMarker(this, startOffset, length); + markers.Add(m); + // no need to mark segment for redraw: the text marker is invisible until a property is set + return m; + } + + public IEnumerable GetMarkersAtOffset(int offset) + { + return markers.FindSegmentsContaining(offset); + } + + public IEnumerable TextMarkers { + get { return markers; } + } + + public void RemoveAll(Predicate predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + foreach (TextMarker m in markers.ToArray()) { + if (predicate(m)) + Remove(m); + } + } + + public void Remove(ITextMarker marker) + { + if (marker == null) + return; + + TextMarker m = marker as TextMarker; + if (markers.Remove(m)) { + Redraw(m); + m.OnDeleted(); + } + } + + /// + /// Redraws the specified text segment. + /// + public void Redraw(ISegment segment) + { + codeEditor.TextArea.TextView.Redraw(segment, DispatcherPriority.Normal); + } + #endregion + + #region DocumentColorizingTransformer + protected override void ColorizeLine(DocumentLine line) + { + if (markers == null) + return; + int lineStart = line.Offset; + int lineEnd = lineStart + line.Length; + foreach (TextMarker marker in markers.FindOverlappingSegments(lineStart, line.Length).Reverse()) { + if (!marker.IsVisible(marker.Bookmark)) + continue; + + Brush foregroundBrush = null; + if (marker.ForegroundColor != null) { + foregroundBrush = new SolidColorBrush(marker.ForegroundColor.Value); + foregroundBrush.Freeze(); + } + ChangeLinePart( + Math.Max(marker.StartOffset, lineStart), + Math.Min(marker.EndOffset, lineEnd), + element => { + if (foregroundBrush != null) { + element.TextRunProperties.SetForegroundBrush(foregroundBrush); + } + } + ); + } + } + #endregion + + #region IBackgroundRenderer + public KnownLayer Layer { + get { + // draw behind selection + return KnownLayer.Selection; + } + } + + public void Draw(ICSharpCode.AvalonEdit.Rendering.TextView textView, DrawingContext drawingContext) + { + if (textView == null) + throw new ArgumentNullException("textView"); + if (drawingContext == null) + throw new ArgumentNullException("drawingContext"); + if (markers == null || !textView.VisualLinesValid) + return; + var visualLines = textView.VisualLines; + if (visualLines.Count == 0) + return; + int viewStart = visualLines.First().FirstDocumentLine.Offset; + int viewEnd = visualLines.Last().LastDocumentLine.Offset + visualLines.Last().LastDocumentLine.Length; + foreach (TextMarker marker in markers.FindOverlappingSegments(viewStart, viewEnd - viewStart).Reverse()) { + if (!marker.IsVisible(marker.Bookmark)) + continue; + + if (marker.BackgroundColor != null) { + BackgroundGeometryBuilder geoBuilder = new BackgroundGeometryBuilder(); + geoBuilder.AlignToWholePixels = true; + geoBuilder.CornerRadius = 3; + geoBuilder.AddSegment(textView, marker); + Geometry geometry = geoBuilder.CreateGeometry(); + if (geometry != null) { + Color color = marker.BackgroundColor.Value; + SolidColorBrush brush = new SolidColorBrush(color); + brush.Freeze(); + drawingContext.DrawGeometry(brush, null, geometry); + } + } + if (marker.MarkerType != TextMarkerType.None) { + foreach (Rect r in BackgroundGeometryBuilder.GetRectsForSegment(textView, marker)) { + Point startPoint = r.BottomLeft; + Point endPoint = r.BottomRight; + + Pen usedPen = new Pen(new SolidColorBrush(marker.MarkerColor), 1); + usedPen.Freeze(); + switch (marker.MarkerType) { + case TextMarkerType.SquigglyUnderline: + double offset = 2.5; + + int count = Math.Max((int)((endPoint.X - startPoint.X) / offset) + 1, 4); + + StreamGeometry geometry = new StreamGeometry(); + + using (StreamGeometryContext ctx = geometry.Open()) { + ctx.BeginFigure(startPoint, false, false); + ctx.PolyLineTo(CreatePoints(startPoint, endPoint, offset, count).ToArray(), true, false); + } + + geometry.Freeze(); + + drawingContext.DrawGeometry(Brushes.Transparent, usedPen, geometry); + break; + } + } + } + } + } + + IEnumerable CreatePoints(Point start, Point end, double offset, int count) + { + for (int i = 0; i < count; i++) + yield return new Point(start.X + i * offset, start.Y - ((i + 1) % 2 == 0 ? offset : 0)); + } + #endregion + } + + sealed class TextMarker : TextSegment, ITextMarker + { + readonly TextMarkerService service; + + public TextMarker(TextMarkerService service, int startOffset, int length) + { + if (service == null) + throw new ArgumentNullException("service"); + this.service = service; + this.StartOffset = startOffset; + this.Length = length; + this.markerType = TextMarkerType.None; + } + + public event EventHandler Deleted; + + public bool IsDeleted { + get { return !this.IsConnectedToCollection; } + } + + public void Delete() + { + service.Remove(this); + } + + internal void OnDeleted() + { + if (Deleted != null) + Deleted(this, EventArgs.Empty); + } + + void Redraw() + { + service.Redraw(this); + } + + Color? backgroundColor; + + public Color? BackgroundColor { + get { return backgroundColor; } + set { + if (backgroundColor != value) { + backgroundColor = value; + Redraw(); + } + } + } + + Color? foregroundColor; + + public Color? ForegroundColor { + get { return foregroundColor; } + set { + if (foregroundColor != value) { + foregroundColor = value; + Redraw(); + } + } + } + + public object Tag { get; set; } + + TextMarkerType markerType; + + public TextMarkerType MarkerType { + get { return markerType; } + set { + if (markerType != value) { + markerType = value; + Redraw(); + } + } + } + + Color markerColor; + + public Color MarkerColor { + get { return markerColor; } + set { + if (markerColor != value) { + markerColor = value; + Redraw(); + } + } + + } + /// + public object ToolTip { get; set; } + + /// + public Predicate IsVisible { get; set; } + + /// + public IBookmark Bookmark { get; set; } + } +} diff --git a/ILSpy/Bookmarks/BookmarkBase.cs b/ILSpy/Bookmarks/BookmarkBase.cs new file mode 100644 index 000000000..f6b5ece5d --- /dev/null +++ b/ILSpy/Bookmarks/BookmarkBase.cs @@ -0,0 +1,97 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Windows.Input; +using System.Windows.Media; + +using ICSharpCode.NRefactory.CSharp; +using Mono.Cecil; + +namespace ICSharpCode.ILSpy.Bookmarks +{ + /// + /// A bookmark that can be attached to an AvalonEdit TextDocument. + /// + public class BookmarkBase : IBookmark + { + AstLocation location; + + protected virtual void RemoveMark() + { + + } + + public AstLocation Location { + get { return location; } + set { location = value; } + } + + public event EventHandler DocumentChanged; + + protected virtual void OnDocumentChanged(EventArgs e) + { + if (DocumentChanged != null) { + DocumentChanged(this, e); + } + } + + protected virtual void Redraw() + { + + } + + public MemberReference MemberReference { get; set; } + + public int LineNumber { + get { return location.Line; } + } + + public int ColumnNumber { + get { return location.Column; } + } + + public virtual int ZOrder { + get { return 0; } + } + + /// + /// Gets if the bookmark can be toggled off using the 'set/unset bookmark' command. + /// + public virtual bool CanToggle { + get { + return true; + } + } + + public BookmarkBase(MemberReference member, AstLocation location) + { + this.MemberReference = member; + this.Location = location; + } + + public virtual ImageSource Image { + get { return null; } + } + + public virtual void MouseDown(MouseButtonEventArgs e) + { + } + + public virtual void MouseUp(MouseButtonEventArgs e) + { + if (e.ChangedButton == MouseButton.Left && CanToggle) { + RemoveMark(); + e.Handled = true; + } + } + + public virtual bool CanDragDrop { + get { return false; } + } + + public virtual void Drop(int lineNumber) + { + } + } +} diff --git a/ILSpy/Bookmarks/BookmarkEventHandler.cs b/ILSpy/Bookmarks/BookmarkEventHandler.cs new file mode 100644 index 000000000..5dae1beac --- /dev/null +++ b/ILSpy/Bookmarks/BookmarkEventHandler.cs @@ -0,0 +1,28 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; + +namespace ICSharpCode.ILSpy.Bookmarks +{ + public delegate void BookmarkEventHandler(object sender, BookmarkEventArgs e); + + /// + /// Description of BookmarkEventHandler. + /// + public class BookmarkEventArgs : EventArgs + { + BookmarkBase bookmark; + + public BookmarkBase Bookmark { + get { + return bookmark; + } + } + + public BookmarkEventArgs(BookmarkBase bookmark) + { + this.bookmark = bookmark; + } + } +} diff --git a/ILSpy/Bookmarks/BookmarkManager.cs b/ILSpy/Bookmarks/BookmarkManager.cs new file mode 100644 index 000000000..65a769617 --- /dev/null +++ b/ILSpy/Bookmarks/BookmarkManager.cs @@ -0,0 +1,116 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using ICSharpCode.Decompiler; +using ICSharpCode.NRefactory.CSharp; +using Mono.Cecil; +using Mono.CSharp; + +namespace ICSharpCode.ILSpy.Bookmarks +{ + /// + /// Static class that maintains the list of bookmarks and breakpoints. + /// + public static partial class BookmarkManager + { + static List bookmarks = new List(); + + public static List Bookmarks { + get { + return bookmarks; + } + } + + public static List GetBookmarks(string typeName) + { + if (typeName == null) + throw new ArgumentNullException("typeName"); + + List marks = new List(); + + foreach (BookmarkBase mark in bookmarks) { + if (typeName == mark.MemberReference.FullName) { + marks.Add(mark); + } + } + + return marks; + } + + public static void AddMark(BookmarkBase bookmark) + { + if (bookmark == null) return; + if (bookmarks.Contains(bookmark)) return; + if (bookmarks.Exists(b => IsEqualBookmark(b, bookmark))) return; + bookmarks.Add(bookmark); + OnAdded(new BookmarkEventArgs(bookmark)); + } + + static bool IsEqualBookmark(BookmarkBase a, BookmarkBase b) + { + if (a == b) + return true; + if (a == null || b == null) + return false; + if (a.GetType() != b.GetType()) + return false; + if (a.MemberReference.FullName != b.MemberReference.FullName) + return false; + return a.LineNumber == b.LineNumber; + } + + public static void RemoveMark(BookmarkBase bookmark) + { + bookmarks.Remove(bookmark); + OnRemoved(new BookmarkEventArgs(bookmark)); + } + + public static void Clear() + { + while (bookmarks.Count > 0) { + var b = bookmarks[bookmarks.Count - 1]; + bookmarks.RemoveAt(bookmarks.Count - 1); + OnRemoved(new BookmarkEventArgs(b)); + } + } + + internal static void Initialize() + { + + } + + static void OnRemoved(BookmarkEventArgs e) + { + if (Removed != null) { + Removed(null, e); + } + } + + static void OnAdded(BookmarkEventArgs e) + { + if (Added != null) { + Added(null, e); + } + } + + public static void ToggleBookmark(string typeName, int line, + Predicate canToggle, + Func bookmarkFactory) + { + foreach (BookmarkBase bookmark in GetBookmarks(typeName)) { + if (canToggle(bookmark) && bookmark.LineNumber == line) { + BookmarkManager.RemoveMark(bookmark); + return; + } + } + + // no bookmark at that line: create a new bookmark + BookmarkManager.AddMark(bookmarkFactory(new AstLocation(line, 0))); + } + + public static event BookmarkEventHandler Removed; + public static event BookmarkEventHandler Added; + } +} diff --git a/ILSpy/Bookmarks/Commands.cs b/ILSpy/Bookmarks/Commands.cs new file mode 100644 index 000000000..021daee1b --- /dev/null +++ b/ILSpy/Bookmarks/Commands.cs @@ -0,0 +1,50 @@ +// Copyright (c) 2011 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 ICSharpCode.ILSpy.AvalonEdit; +using ICSharpCode.ILSpy.TreeNodes.Analyzer; +using Mono.Cecil; + +namespace ICSharpCode.ILSpy.Bookmarks +{ + [ExportBookmarkContextMenuEntry(Header = "Analyze", Icon = "images/Search.png", Category="Default")] + internal sealed class AnalyzeBookmarkEntry : IBookmarkContextMenuEntry + { + public bool IsVisible(IBookmark[] marks) + { + return true; + } + + public bool IsEnabled(IBookmark[] marks) + { + return true; + } + + public void Execute(IBookmark[] marks) + { + foreach (var mark in marks) { + if (!(mark is MemberBookmark)) + continue; + + var member = (mark as MemberBookmark).Node.Annotation(); + AnalyzeContextMenuEntry.Analyze(member); + } + } + } +} diff --git a/ILSpy/Bookmarks/IBookmark.cs b/ILSpy/Bookmarks/IBookmark.cs new file mode 100644 index 000000000..37733789a --- /dev/null +++ b/ILSpy/Bookmarks/IBookmark.cs @@ -0,0 +1,69 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Windows.Input; +using System.Windows.Media; + +namespace ICSharpCode.ILSpy.Bookmarks +{ + /// + /// The bookmark margin. + /// + public interface IBookmarkMargin + { + /// + /// Gets the list of bookmarks. + /// + IList Bookmarks { get; } + + /// + /// Redraws the bookmark margin. Bookmarks need to call this method when the Image changes. + /// + void Redraw(); + } + + /// + /// Represents a bookmark in the bookmark margin. + /// + public interface IBookmark + { + /// + /// Gets the line number of the bookmark. + /// + int LineNumber { get; } + + /// + /// Gets the image. + /// + ImageSource Image { get; } + + /// + /// Gets the Z-Order of the bookmark icon. + /// + int ZOrder { get; } + + /// + /// Handles the mouse down event. + /// + void MouseDown(MouseButtonEventArgs e); + + /// + /// Handles the mouse up event. + /// + void MouseUp(MouseButtonEventArgs e); + + /// + /// Gets whether this bookmark can be dragged around. + /// + bool CanDragDrop { get; } + + /// + /// Notifies the bookmark that it was dropped on the specified line. + /// + void Drop(int lineNumber); + } +} diff --git a/ILSpy/Bookmarks/MarkerBookmark.cs b/ILSpy/Bookmarks/MarkerBookmark.cs new file mode 100644 index 000000000..4a587b855 --- /dev/null +++ b/ILSpy/Bookmarks/MarkerBookmark.cs @@ -0,0 +1,21 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using ICSharpCode.NRefactory.CSharp; +using ICSharpCode.ILSpy.AvalonEdit; +using Mono.Cecil; + +namespace ICSharpCode.ILSpy.Bookmarks +{ + public abstract class MarkerBookmark : BookmarkBase + { + public MarkerBookmark(MemberReference member, AstLocation location) : base(member, location) + { + } + + public ITextMarker Marker { get; set; } + + public abstract ITextMarker CreateMarker(ITextMarkerService markerService, int offset, int length); + } +} diff --git a/ILSpy/Bookmarks/MemberBookmark.cs b/ILSpy/Bookmarks/MemberBookmark.cs new file mode 100644 index 000000000..646587ae6 --- /dev/null +++ b/ILSpy/Bookmarks/MemberBookmark.cs @@ -0,0 +1,157 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Windows; +using System.Windows.Input; +using System.Windows.Media; + +using ICSharpCode.NRefactory.CSharp; +using ICSharpCode.NRefactory.TypeSystem; +using Mono.Cecil; + +namespace ICSharpCode.ILSpy.Bookmarks +{ + /// + /// Bookmark used to give additional operations for class members. + /// Does not derive from SDBookmark because it is not stored in the central BookmarkManager, + /// but only in the document's BookmarkManager. + /// + public class MemberBookmark : IBookmark + { + AstNode node; + + public AstNode Node { + get { + return node; + } + } + + public MemberBookmark(AstNode node) + { + this.node = node; + } + + public virtual ImageSource Image { + get { + var attrNode = (AttributedNode)node; + if (node is EnumMemberDeclaration) + return GetMemberOverlayedImage(attrNode, MemberIcon.EnumValue); + + if (node is FieldDeclaration) + return GetMemberOverlayedImage(attrNode, MemberIcon.Field); + + if (node is PropertyDeclaration) + return GetMemberOverlayedImage(attrNode, MemberIcon.Property); + + if (node is EventDeclaration || node is CustomEventDeclaration) + return GetMemberOverlayedImage(attrNode, MemberIcon.Event); + + if (node is IndexerDeclaration) + return GetMemberOverlayedImage(attrNode, MemberIcon.Indexer); + + if (node is OperatorDeclaration) + return GetMemberOverlayedImage(attrNode, MemberIcon.Operator); + + if (node is ConstructorDeclaration || node is DestructorDeclaration) + return GetMemberOverlayedImage(attrNode, MemberIcon.Constructor); + + return GetMemberOverlayedImage(attrNode, MemberIcon.Method); + } + } + + ImageSource GetMemberOverlayedImage(AttributedNode attrNode, MemberIcon icon) + { + switch (attrNode.Modifiers & Modifiers.VisibilityMask) { + case Modifiers.Protected: + return Images.GetIcon(icon, AccessOverlayIcon.Protected, (attrNode.Modifiers & Modifiers.Static) == Modifiers.Static); + case Modifiers.Private: + return Images.GetIcon(icon, AccessOverlayIcon.Private, (attrNode.Modifiers & Modifiers.Static) == Modifiers.Static); + case Modifiers.Internal: + return Images.GetIcon(icon, AccessOverlayIcon.Internal, (attrNode.Modifiers & Modifiers.Static) == Modifiers.Static); + } + + return Images.GetIcon(icon, AccessOverlayIcon.Public, (attrNode.Modifiers & Modifiers.Static) == Modifiers.Static); + } + + public int LineNumber { + get { + var t = node.Annotation>(); + if (t != null) + return t.Item1; + return 0; + } + } + + public virtual void MouseDown(MouseButtonEventArgs e) + { + } + + public virtual void MouseUp(MouseButtonEventArgs e) + { + } + + int IBookmark.ZOrder { + get { return -10; } + } + + bool IBookmark.CanDragDrop { + get { return false; } + } + + void IBookmark.Drop(int lineNumber) + { + throw new NotSupportedException(); + } + } + + public class TypeBookmark : MemberBookmark + { + public TypeBookmark(AstNode node) : base (node) + { + } + + public override ImageSource Image { + get { + var attrNode = (AttributedNode)Node; + + if (Node is DelegateDeclaration) + return GetTypeOverlayedImage(attrNode, TypeIcon.Delegate); + + if (Node is TypeDeclaration) { + var n = Node as TypeDeclaration; + switch (n.ClassType) + { + case ClassType.Delegate: + return GetTypeOverlayedImage(attrNode, TypeIcon.Delegate); + case ClassType.Enum: + return GetTypeOverlayedImage(attrNode, TypeIcon.Enum); + case ClassType.Struct: + return GetTypeOverlayedImage(attrNode, TypeIcon.Struct); + case ClassType.Interface: + return GetTypeOverlayedImage(attrNode, TypeIcon.Interface); + } + } + + if ((attrNode.Modifiers & Modifiers.Static) == Modifiers.Static) + return GetTypeOverlayedImage(attrNode, TypeIcon.StaticClass); + + return GetTypeOverlayedImage(attrNode, TypeIcon.Class); + } + } + + ImageSource GetTypeOverlayedImage(AttributedNode attrNode, TypeIcon icon) + { + switch (attrNode.Modifiers & Modifiers.VisibilityMask) { + case Modifiers.Protected: + return Images.GetIcon(icon, AccessOverlayIcon.Protected); + case Modifiers.Private: + return Images.GetIcon(icon, AccessOverlayIcon.Private); + case Modifiers.Internal: + return Images.GetIcon(icon, AccessOverlayIcon.Internal); + } + + return Images.GetIcon(icon, AccessOverlayIcon.Public); + } + } +} diff --git a/ILSpy/Commands.cs b/ILSpy/Commands.cs deleted file mode 100644 index 8d89e9afc..000000000 --- a/ILSpy/Commands.cs +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright (c) 2011 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.ComponentModel.Composition; -using System.Diagnostics; -using System.Linq; -using System.Threading.Tasks; -using System.Windows.Input; -using ICSharpCode.ILSpy.TextView; -using ICSharpCode.ILSpy.TreeNodes; - -namespace ICSharpCode.ILSpy -{ - [ExportMainMenuCommand(Menu = "_File", Header = "E_xit", MenuOrder = 99999, MenuCategory = "Exit")] - sealed class ExitCommand : SimpleCommand - { - public override void Execute(object parameter) - { - MainWindow.Instance.Close(); - } - } - - [ExportToolbarCommand(ToolTip = "Back", ToolbarIcon = "Images/Back.png", ToolbarCategory = "Navigation", ToolbarOrder = 0)] - sealed class BrowseBackCommand : CommandWrapper { - public BrowseBackCommand() : base(NavigationCommands.BrowseBack) {} - } - - [ExportToolbarCommand(ToolTip = "Forward", ToolbarIcon = "Images/Forward.png", ToolbarCategory = "Navigation", ToolbarOrder = 1)] - sealed class BrowseForwardCommand : CommandWrapper { - public BrowseForwardCommand() : base(NavigationCommands.BrowseForward) {} - } - - [ExportToolbarCommand(ToolTip = "Open", ToolbarIcon = "Images/Open.png", ToolbarCategory = "Open", ToolbarOrder = 0)] - [ExportMainMenuCommand(Menu = "_File", MenuIcon = "Images/Open.png", MenuCategory = "Open", MenuOrder = 0)] - sealed class OpenCommand : CommandWrapper { - public OpenCommand() : base(ApplicationCommands.Open) {} - } - - [ExportMainMenuCommand(Menu = "_File", Header = "Open from _GAC", MenuCategory = "Open", MenuOrder = 1)] - sealed class OpenFromGacCommand : SimpleCommand - { - public override void Execute(object parameter) - { - OpenFromGacDialog dlg = new OpenFromGacDialog(); - dlg.Owner = MainWindow.Instance; - if (dlg.ShowDialog() == true) { - MainWindow.Instance.OpenFiles(dlg.SelectedFileNames); - } - } - } - - [ExportToolbarCommand(ToolTip = "Reload all assemblies", ToolbarIcon = "Images/Refresh.png", ToolbarCategory = "Open", ToolbarOrder = 2)] - [ExportMainMenuCommand(Menu = "_File", Header = "Reload", MenuIcon = "Images/Refresh.png", MenuCategory = "Open", MenuOrder = 2)] - sealed class RefreshCommand : CommandWrapper { - public RefreshCommand() : base(NavigationCommands.Refresh) {} - } - - [ExportMainMenuCommand(Menu = "_File", Header = "_Save Code...", MenuIcon = "Images/SaveFile.png", MenuCategory = "Save", MenuOrder = 0)] - sealed class SaveCommand : CommandWrapper - { - public SaveCommand() : base(ApplicationCommands.Save) {} - } - - #if DEBUG - [ExportMainMenuCommand(Menu = "_File", Header = "DEBUG -- Decompile All", MenuCategory = "Open", MenuOrder = 2.5)] - sealed class DecompileAllCommand : SimpleCommand - { - public override bool CanExecute(object parameter) - { - return System.IO.Directory.Exists("c:\\temp\\decompiled"); - } - - public override void Execute(object parameter) - { - MainWindow.Instance.TextView.RunWithCancellation( - ct => Task.Factory.StartNew( - () => { - AvalonEditTextOutput output = new AvalonEditTextOutput(); - Parallel.ForEach( - MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), - new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct }, - delegate (LoadedAssembly asm) { - if (!asm.HasLoadError) { - Stopwatch w = Stopwatch.StartNew(); - Exception exception = null; - using (var writer = new System.IO.StreamWriter("c:\\temp\\decompiled\\" + asm.ShortName + ".cs")) { - try { - new CSharpLanguage().DecompileAssembly( - asm, new Decompiler.PlainTextOutput(writer), - new DecompilationOptions { FullDecompilation = true, CancellationToken = ct }); - } catch (Exception ex) { - writer.WriteLine(ex.ToString()); - exception = ex; - } - } - lock (output) { - output.Write(asm.ShortName + " - " + w.Elapsed); - if (exception != null) { - output.Write(" - "); - output.Write(exception.GetType().Name); - } - output.WriteLine(); - } - } - }); - return output; - } - ), - task => MainWindow.Instance.TextView.ShowText(task.Result)); - } - } - #endif - - class CommandWrapper : ICommand - { - ICommand wrappedCommand; - - public CommandWrapper(ICommand wrappedCommand) - { - this.wrappedCommand = wrappedCommand; - } - - public static ICommand Unwrap(ICommand command) - { - CommandWrapper w = command as CommandWrapper; - if (w != null) - return w.wrappedCommand; - else - return command; - } - - public event EventHandler CanExecuteChanged { - add { wrappedCommand.CanExecuteChanged += value; } - remove { wrappedCommand.CanExecuteChanged -= value; } - } - - public void Execute(object parameter) - { - wrappedCommand.Execute(parameter); - } - - public bool CanExecute(object parameter) - { - return wrappedCommand.CanExecute(parameter); - } - } - - public abstract class SimpleCommand : ICommand - { - public event EventHandler CanExecuteChanged { - add { CommandManager.RequerySuggested += value; } - remove { CommandManager.RequerySuggested -= value; } - } - - public abstract void Execute(object parameter); - - public virtual bool CanExecute(object parameter) - { - return true; - } - } -} diff --git a/ILSpy/Commands/BrowseBackCommand.cs b/ILSpy/Commands/BrowseBackCommand.cs new file mode 100644 index 000000000..e5af95db8 --- /dev/null +++ b/ILSpy/Commands/BrowseBackCommand.cs @@ -0,0 +1,32 @@ +// Copyright (c) 2011 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.Windows.Input; + +namespace ICSharpCode.ILSpy +{ + [ExportToolbarCommand(ToolTip = "Back", ToolbarIcon = "Images/Back.png", ToolbarCategory = "Navigation", ToolbarOrder = 0)] + sealed class BrowseBackCommand : CommandWrapper + { + public BrowseBackCommand() + : base(NavigationCommands.BrowseBack) + { + } + } +} diff --git a/ILSpy/Commands/BrowseForwardCommand.cs b/ILSpy/Commands/BrowseForwardCommand.cs new file mode 100644 index 000000000..1e0db84a7 --- /dev/null +++ b/ILSpy/Commands/BrowseForwardCommand.cs @@ -0,0 +1,32 @@ +// Copyright (c) 2011 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.Windows.Input; + +namespace ICSharpCode.ILSpy +{ + [ExportToolbarCommand(ToolTip = "Forward", ToolbarIcon = "Images/Forward.png", ToolbarCategory = "Navigation", ToolbarOrder = 1)] + sealed class BrowseForwardCommand : CommandWrapper + { + public BrowseForwardCommand() + : base(NavigationCommands.BrowseForward) + { + } + } +} diff --git a/ILSpy/Commands/CommandWrapper.cs b/ILSpy/Commands/CommandWrapper.cs new file mode 100644 index 000000000..f3d89138c --- /dev/null +++ b/ILSpy/Commands/CommandWrapper.cs @@ -0,0 +1,58 @@ +// Copyright (c) 2011 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.Windows.Input; + +namespace ICSharpCode.ILSpy +{ + class CommandWrapper : ICommand + { + private ICommand wrappedCommand; + + public CommandWrapper(ICommand wrappedCommand) + { + this.wrappedCommand = wrappedCommand; + } + + public static ICommand Unwrap(ICommand command) + { + CommandWrapper w = command as CommandWrapper; + if (w != null) + return w.wrappedCommand; + else + return command; + } + + public event EventHandler CanExecuteChanged + { + add { wrappedCommand.CanExecuteChanged += value; } + remove { wrappedCommand.CanExecuteChanged -= value; } + } + + public void Execute(object parameter) + { + wrappedCommand.Execute(parameter); + } + + public bool CanExecute(object parameter) + { + return wrappedCommand.CanExecute(parameter); + } + } +} diff --git a/ILSpy/Commands/DecompileAllCommand.cs b/ILSpy/Commands/DecompileAllCommand.cs new file mode 100644 index 000000000..542c155e4 --- /dev/null +++ b/ILSpy/Commands/DecompileAllCommand.cs @@ -0,0 +1,69 @@ +// Copyright (c) 2011 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. + +#if DEBUG + +using System; +using System.Diagnostics; +using System.Threading.Tasks; +using ICSharpCode.ILSpy.TextView; + +namespace ICSharpCode.ILSpy +{ + [ExportMainMenuCommand(Menu = "_File", Header = "DEBUG -- Decompile All", MenuCategory = "Open", MenuOrder = 2.5)] + sealed class DecompileAllCommand : SimpleCommand + { + public override bool CanExecute(object parameter) + { + return System.IO.Directory.Exists("c:\\temp\\decompiled"); + } + + public override void Execute(object parameter) + { + MainWindow.Instance.TextView.RunWithCancellation(ct => Task.Factory.StartNew(() => { + AvalonEditTextOutput output = new AvalonEditTextOutput(); + Parallel.ForEach(MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct }, delegate(LoadedAssembly asm) { + if (!asm.HasLoadError) { + Stopwatch w = Stopwatch.StartNew(); + Exception exception = null; + using (var writer = new System.IO.StreamWriter("c:\\temp\\decompiled\\" + asm.ShortName + ".cs")) { + try { + new CSharpLanguage().DecompileAssembly(asm, new Decompiler.PlainTextOutput(writer), new DecompilationOptions { FullDecompilation = true, CancellationToken = ct }); + } + catch (Exception ex) { + writer.WriteLine(ex.ToString()); + exception = ex; + } + } + lock (output) { + output.Write(asm.ShortName + " - " + w.Elapsed); + if (exception != null) { + output.Write(" - "); + output.Write(exception.GetType().Name); + } + output.WriteLine(); + } + } + }); + return output; + }), task => MainWindow.Instance.TextView.ShowText(task.Result)); + } + } +} + +#endif \ No newline at end of file diff --git a/ILSpy/Commands/ExitCommand.cs b/ILSpy/Commands/ExitCommand.cs new file mode 100644 index 000000000..d064aa557 --- /dev/null +++ b/ILSpy/Commands/ExitCommand.cs @@ -0,0 +1,31 @@ +// Copyright (c) 2011 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; + +namespace ICSharpCode.ILSpy +{ + [ExportMainMenuCommand(Menu = "_File", Header = "E_xit", MenuOrder = 99999, MenuCategory = "Exit")] + sealed class ExitCommand : SimpleCommand + { + public override void Execute(object parameter) + { + MainWindow.Instance.Close(); + } + } +} \ No newline at end of file diff --git a/ILSpy/ExportCommandAttribute.cs b/ILSpy/Commands/ExportCommandAttribute.cs similarity index 100% rename from ILSpy/ExportCommandAttribute.cs rename to ILSpy/Commands/ExportCommandAttribute.cs diff --git a/ILSpy/Commands/OpenCommand.cs b/ILSpy/Commands/OpenCommand.cs new file mode 100644 index 000000000..74030365f --- /dev/null +++ b/ILSpy/Commands/OpenCommand.cs @@ -0,0 +1,33 @@ +// Copyright (c) 2011 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.Windows.Input; + +namespace ICSharpCode.ILSpy +{ + [ExportToolbarCommand(ToolTip = "Open", ToolbarIcon = "Images/Open.png", ToolbarCategory = "Open", ToolbarOrder = 0)] + [ExportMainMenuCommand(Menu = "_File", MenuIcon = "Images/Open.png", MenuCategory = "Open", MenuOrder = 0)] + sealed class OpenCommand : CommandWrapper + { + public OpenCommand() + : base(ApplicationCommands.Open) + { + } + } +} diff --git a/ILSpy/Commands/OpenFromGacCommand.cs b/ILSpy/Commands/OpenFromGacCommand.cs new file mode 100644 index 000000000..ceeff5bdc --- /dev/null +++ b/ILSpy/Commands/OpenFromGacCommand.cs @@ -0,0 +1,34 @@ +// Copyright (c) 2011 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; + +namespace ICSharpCode.ILSpy +{ + [ExportMainMenuCommand(Menu = "_File", Header = "Open from _GAC", MenuCategory = "Open", MenuOrder = 1)] + sealed class OpenFromGacCommand : SimpleCommand + { + public override void Execute(object parameter) + { + OpenFromGacDialog dlg = new OpenFromGacDialog(); + dlg.Owner = MainWindow.Instance; + if (dlg.ShowDialog() == true) + MainWindow.Instance.OpenFiles(dlg.SelectedFileNames); + } + } +} diff --git a/ILSpy/Commands/RefreshCommand.cs b/ILSpy/Commands/RefreshCommand.cs new file mode 100644 index 000000000..21edd69c5 --- /dev/null +++ b/ILSpy/Commands/RefreshCommand.cs @@ -0,0 +1,33 @@ +// Copyright (c) 2011 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.Windows.Input; + +namespace ICSharpCode.ILSpy +{ + [ExportToolbarCommand(ToolTip = "Reload all assemblies", ToolbarIcon = "Images/Refresh.png", ToolbarCategory = "Open", ToolbarOrder = 2)] + [ExportMainMenuCommand(Menu = "_File", Header = "Reload", MenuIcon = "Images/Refresh.png", MenuCategory = "Open", MenuOrder = 2)] + sealed class RefreshCommand : CommandWrapper + { + public RefreshCommand() + : base(NavigationCommands.Refresh) + { + } + } +} diff --git a/ILSpy/Commands/SaveCommand.cs b/ILSpy/Commands/SaveCommand.cs new file mode 100644 index 000000000..63989d863 --- /dev/null +++ b/ILSpy/Commands/SaveCommand.cs @@ -0,0 +1,32 @@ +// Copyright (c) 2011 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.Windows.Input; + +namespace ICSharpCode.ILSpy +{ + [ExportMainMenuCommand(Menu = "_File", Header = "_Save Code...", MenuIcon = "Images/SaveFile.png", MenuCategory = "Save", MenuOrder = 0)] + sealed class SaveCommand : CommandWrapper + { + public SaveCommand() + : base(ApplicationCommands.Save) + { + } + } +} diff --git a/ILSpy/Commands/ShowAnalyzerCommand.cs b/ILSpy/Commands/ShowAnalyzerCommand.cs new file mode 100644 index 000000000..1dcc74c6d --- /dev/null +++ b/ILSpy/Commands/ShowAnalyzerCommand.cs @@ -0,0 +1,31 @@ +// Copyright (c) 2011 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; + +namespace ICSharpCode.ILSpy +{ + [ExportMainMenuCommand(Menu = "_View", Header = "_Analyzer", MenuCategory = "ShowPane", MenuOrder = 100)] + sealed class ShowAnalyzerCommand : SimpleCommand + { + public override void Execute(object parameter) + { + AnalyzerTreeView.Instance.Show(); + } + } +} diff --git a/ILSpy/Commands/SimpleCommand.cs b/ILSpy/Commands/SimpleCommand.cs new file mode 100644 index 000000000..d8490a1a6 --- /dev/null +++ b/ILSpy/Commands/SimpleCommand.cs @@ -0,0 +1,39 @@ +// Copyright (c) 2011 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.Windows.Input; + +namespace ICSharpCode.ILSpy +{ + public abstract class SimpleCommand : ICommand + { + public event EventHandler CanExecuteChanged + { + add { CommandManager.RequerySuggested += value; } + remove { CommandManager.RequerySuggested -= value; } + } + + public abstract void Execute(object parameter); + + public virtual bool CanExecute(object parameter) + { + return true; + } + } +} diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index 4e99f674b..d35d9fd11 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -93,19 +93,36 @@ + + + + + + + + + + + + + + + - + + + DecompilerSettingsPanel.xaml Code - + - + @@ -113,19 +130,23 @@ - - + + + - + + + + OpenFromGacDialog.xaml Code @@ -135,6 +156,8 @@ Code + + Code @@ -142,6 +165,8 @@ DisplaySettingsPanel.xaml Code + + @@ -158,8 +183,16 @@ + + + - + + + + + + @@ -218,9 +251,9 @@ - + - + @@ -312,5 +345,15 @@ ICSharpCode.TreeView + + + + + + + + + + \ No newline at end of file diff --git a/ILSpy/IPane.cs b/ILSpy/IPane.cs new file mode 100644 index 000000000..2447f8bdd --- /dev/null +++ b/ILSpy/IPane.cs @@ -0,0 +1,27 @@ +// Copyright (c) 2011 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; + +namespace ICSharpCode.ILSpy +{ + public interface IPane + { + void Closed(); + } +} diff --git a/ILSpy/Images/Delete.png b/ILSpy/Images/Delete.png index 824d938dd..560865f30 100644 Binary files a/ILSpy/Images/Delete.png and b/ILSpy/Images/Delete.png differ diff --git a/ILSpy/Images/Images.cs b/ILSpy/Images/Images.cs index 8755e772e..8e8f2b08d 100644 --- a/ILSpy/Images/Images.cs +++ b/ILSpy/Images/Images.cs @@ -59,6 +59,9 @@ namespace ICSharpCode.ILSpy public static readonly BitmapImage Resource = LoadBitmap("Resource"); public static readonly BitmapImage ResourceImage = LoadBitmap("ResourceImage"); public static readonly BitmapImage ResourceResourcesFile = LoadBitmap("ResourceResourcesFile"); + public static readonly BitmapImage ResourceXml = LoadBitmap("ResourceXml"); + public static readonly BitmapImage ResourceXsd = LoadBitmap("ResourceXsd"); + public static readonly BitmapImage ResourceXslt = LoadBitmap("ResourceXslt"); public static readonly BitmapImage Class = LoadBitmap("Class"); public static readonly BitmapImage Struct = LoadBitmap("Struct"); diff --git a/ILSpy/Images/OverlayInternal.png b/ILSpy/Images/OverlayInternal.png index fcc53bdde..3c1cbc606 100644 Binary files a/ILSpy/Images/OverlayInternal.png and b/ILSpy/Images/OverlayInternal.png differ diff --git a/ILSpy/Images/OverlayProtected.png b/ILSpy/Images/OverlayProtected.png index 0d125f7d2..8f789baff 100644 Binary files a/ILSpy/Images/OverlayProtected.png and b/ILSpy/Images/OverlayProtected.png differ diff --git a/ILSpy/Images/OverlayProtectedInternal.png b/ILSpy/Images/OverlayProtectedInternal.png index dacce33c4..82676812f 100644 Binary files a/ILSpy/Images/OverlayProtectedInternal.png and b/ILSpy/Images/OverlayProtectedInternal.png differ diff --git a/ILSpy/Images/ResourceXml.png b/ILSpy/Images/ResourceXml.png new file mode 100644 index 000000000..157feead5 Binary files /dev/null and b/ILSpy/Images/ResourceXml.png differ diff --git a/ILSpy/Images/ResourceXsd.png b/ILSpy/Images/ResourceXsd.png new file mode 100644 index 000000000..4422be59e Binary files /dev/null and b/ILSpy/Images/ResourceXsd.png differ diff --git a/ILSpy/Images/ResourceXslt.png b/ILSpy/Images/ResourceXslt.png new file mode 100644 index 000000000..107c9f2a6 Binary files /dev/null and b/ILSpy/Images/ResourceXslt.png differ diff --git a/ILSpy/CSharpLanguage.cs b/ILSpy/Languages/CSharpLanguage.cs similarity index 93% rename from ILSpy/CSharpLanguage.cs rename to ILSpy/Languages/CSharpLanguage.cs index ad3ee5ae1..0c628bb6f 100644 --- a/ILSpy/CSharpLanguage.cs +++ b/ILSpy/Languages/CSharpLanguage.cs @@ -23,11 +23,11 @@ using System.ComponentModel.Composition; using System.IO; using System.Linq; using System.Resources; -using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Xaml; using System.Xml; + using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Ast; using ICSharpCode.Decompiler.Ast.Transforms; @@ -100,6 +100,7 @@ namespace ICSharpCode.ILSpy codeDomBuilder.AddMethod(method); RunTransformsAndGenerateCode(codeDomBuilder, output, options); } + NotifyDecompilationFinished(codeDomBuilder); } class SelectCtorTransform : IAstTransform @@ -144,6 +145,7 @@ namespace ICSharpCode.ILSpy AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: property.DeclaringType, isSingleMember: true); codeDomBuilder.AddProperty(property); RunTransformsAndGenerateCode(codeDomBuilder, output, options); + NotifyDecompilationFinished(codeDomBuilder); } public override void DecompileField(FieldDefinition field, ITextOutput output, DecompilationOptions options) @@ -157,6 +159,7 @@ namespace ICSharpCode.ILSpy AddFieldsAndCtors(codeDomBuilder, field.DeclaringType, field.IsStatic); } RunTransformsAndGenerateCode(codeDomBuilder, output, options, new SelectFieldTransform(field)); + NotifyDecompilationFinished(codeDomBuilder); } /// @@ -200,6 +203,7 @@ namespace ICSharpCode.ILSpy AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: ev.DeclaringType, isSingleMember: true); codeDomBuilder.AddEvent(ev); RunTransformsAndGenerateCode(codeDomBuilder, output, options); + NotifyDecompilationFinished(codeDomBuilder); } public override void DecompileType(TypeDefinition type, ITextOutput output, DecompilationOptions options) @@ -207,6 +211,7 @@ namespace ICSharpCode.ILSpy AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: type); codeDomBuilder.AddType(type); RunTransformsAndGenerateCode(codeDomBuilder, output, options); + NotifyDecompilationFinished(codeDomBuilder); } void RunTransformsAndGenerateCode(AstBuilder astBuilder, ITextOutput output, DecompilationOptions options, IAstTransform additionalTransform = null) @@ -230,6 +235,46 @@ namespace ICSharpCode.ILSpy WriteProjectFile(new TextOutputWriter(output), files, assembly.AssemblyDefinition.MainModule); } else { base.DecompileAssembly(assembly, output, options); + output.WriteLine(); + ModuleDefinition mainModule = assembly.AssemblyDefinition.MainModule; + if (mainModule.EntryPoint != null) { + output.Write("// Entry point: "); + output.WriteReference(mainModule.EntryPoint.DeclaringType.FullName + "." + mainModule.EntryPoint.Name, mainModule.EntryPoint); + output.WriteLine(); + } + switch (mainModule.Architecture) { + case TargetArchitecture.I386: + if ((mainModule.Attributes & ModuleAttributes.Required32Bit) == ModuleAttributes.Required32Bit) + output.WriteLine("// Architecture: x86"); + else + output.WriteLine("// Architecture: AnyCPU"); + break; + case TargetArchitecture.AMD64: + output.WriteLine("// Architecture: x64"); + break; + case TargetArchitecture.IA64: + output.WriteLine("// Architecture: Itanium-64"); + break; + } + if ((mainModule.Attributes & ModuleAttributes.ILOnly) == 0) { + output.WriteLine("// This assembly contains unmanaged code."); + } + switch (mainModule.Runtime) { + case TargetRuntime.Net_1_0: + output.WriteLine("// Runtime: .NET 1.0"); + break; + case TargetRuntime.Net_1_1: + output.WriteLine("// Runtime: .NET 1.1"); + break; + case TargetRuntime.Net_2_0: + output.WriteLine("// Runtime: .NET 2.0"); + break; + case TargetRuntime.Net_4_0: + output.WriteLine("// Runtime: .NET 4.0"); + break; + } + output.WriteLine(); + // don't automatically load additional assemblies when an assembly node is selected in the tree view using (options.FullDecompilation ? null : LoadedAssembly.DisableAssemblyLoad()) { AstBuilder codeDomBuilder = CreateAstBuilder(options, currentModule: assembly.AssemblyDefinition.MainModule); @@ -238,6 +283,7 @@ namespace ICSharpCode.ILSpy codeDomBuilder.GenerateCode(output); } } + OnDecompilationFinished(null); } #region WriteProjectFile diff --git a/ILSpy/ILAstLanguage.cs b/ILSpy/Languages/ILAstLanguage.cs similarity index 100% rename from ILSpy/ILAstLanguage.cs rename to ILSpy/Languages/ILAstLanguage.cs diff --git a/ILSpy/ILLanguage.cs b/ILSpy/Languages/ILLanguage.cs similarity index 87% rename from ILSpy/ILLanguage.cs rename to ILSpy/Languages/ILLanguage.cs index 7578f107f..cf497476f 100644 --- a/ILSpy/ILLanguage.cs +++ b/ILSpy/Languages/ILLanguage.cs @@ -18,8 +18,6 @@ using System; using System.Collections.Generic; -using System.IO; - using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Disassembler; using Mono.Cecil; @@ -52,12 +50,16 @@ namespace ICSharpCode.ILSpy public override void DecompileMethod(MethodDefinition method, ITextOutput output, DecompilationOptions options) { - new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken).DisassembleMethod(method); + var dis = new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken); + dis.DisassembleMethod(method); + NotifyDecompilationFinished(dis); } public override void DecompileField(FieldDefinition field, ITextOutput output, DecompilationOptions options) { - new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken).DisassembleField(field); + var dis = new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken); + dis.DisassembleField(field); + NotifyDecompilationFinished(dis); } public override void DecompileProperty(PropertyDefinition property, ITextOutput output, DecompilationOptions options) @@ -76,6 +78,7 @@ namespace ICSharpCode.ILSpy output.WriteLine(); rd.DisassembleMethod(m); } + NotifyDecompilationFinished(rd); } public override void DecompileEvent(EventDefinition ev, ITextOutput output, DecompilationOptions options) @@ -94,16 +97,20 @@ namespace ICSharpCode.ILSpy output.WriteLine(); rd.DisassembleMethod(m); } + NotifyDecompilationFinished(rd); } public override void DecompileType(TypeDefinition type, ITextOutput output, DecompilationOptions options) { - new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken).DisassembleType(type); + var dis = new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken); + dis.DisassembleType(type); + NotifyDecompilationFinished(dis); } public override void DecompileNamespace(string nameSpace, IEnumerable types, ITextOutput output, DecompilationOptions options) { new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken).DisassembleNamespace(nameSpace, types); + OnDecompilationFinished(null); } public override void DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) @@ -122,6 +129,7 @@ namespace ICSharpCode.ILSpy output.WriteLine(); rd.WriteModuleContents(assembly.AssemblyDefinition.MainModule); } + OnDecompilationFinished(null); } public override string TypeToString(TypeReference t, bool includeNamespace, ICustomAttributeProvider attributeProvider) diff --git a/ILSpy/Language.cs b/ILSpy/Languages/Language.cs similarity index 68% rename from ILSpy/Language.cs rename to ILSpy/Languages/Language.cs index d9f566013..3275e54cd 100644 --- a/ILSpy/Language.cs +++ b/ILSpy/Languages/Language.cs @@ -17,20 +17,56 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Collections.Concurrent; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel.Composition.Hosting; using System.Linq; + using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.Ast; +using ICSharpCode.Decompiler.Disassembler; +using ICSharpCode.Decompiler.ILAst; +using ICSharpCode.NRefactory.CSharp; +using ICSharpCode.NRefactory.Utils; using Mono.Cecil; namespace ICSharpCode.ILSpy { + /// + /// Decompilation event arguments. + /// + public sealed class DecompileEventArgs : EventArgs + { + /// + /// Gets ot sets the code mappings + /// + public Dictionary> CodeMappings { get; internal set; } + + /// + /// Gets or sets the local variables. + /// + public ConcurrentDictionary> LocalVariables { get; internal set; } + + /// + /// Gets the list of MembeReferences that are decompiled (TypeDefinitions, MethodDefinitions, etc) + /// + public Dictionary DecompiledMemberReferences { get; internal set; } + + /// + /// Gets (or internal sets) the AST nodes. + /// + public IEnumerable AstNodes { get; internal set; } + } + /// /// Base class for language-specific decompiler implementations. /// public abstract class Language { + /// + /// Decompile finished event. + /// + public event EventHandler DecompileFinished; + /// /// Gets the name of the language (as shown in the UI) /// @@ -85,6 +121,7 @@ namespace ICSharpCode.ILSpy public virtual void DecompileNamespace(string nameSpace, IEnumerable types, ITextOutput output, DecompilationOptions options) { WriteCommentLine(output, nameSpace); + OnDecompilationFinished(null); } public virtual void DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) @@ -155,43 +192,39 @@ namespace ICSharpCode.ILSpy { return member; } - } - - public static class Languages - { - static ReadOnlyCollection allLanguages; - - /// - /// A list of all languages. - /// - public static ReadOnlyCollection AllLanguages + + protected virtual void OnDecompilationFinished(DecompileEventArgs e) { - get - { - return allLanguages; + if (DecompileFinished != null) { + DecompileFinished(this, e); } } - - - internal static void Initialize(CompositionContainer composition) - { - List languages = new List(); - languages.AddRange(composition.GetExportedValues()); - languages.Add(new ILLanguage(true)); -#if DEBUG - languages.AddRange(ILAstLanguage.GetDebugLanguages()); - languages.AddRange(CSharpLanguage.GetDebugLanguages()); -#endif - allLanguages = languages.AsReadOnly(); - } - - /// - /// Gets a language using its name. - /// If the language is not found, C# is returned instead. - /// - public static Language GetLanguage(string name) - { - return AllLanguages.FirstOrDefault(l => l.Name == name) ?? AllLanguages.First(); + + protected void NotifyDecompilationFinished(BaseCodeMappings b) + { + if (b is AstBuilder) { + var builder = b as AstBuilder; + + var nodes = TreeTraversal + .PreOrder((AstNode)builder.CompilationUnit, n => n.Children) + .Where(n => n is AttributedNode && n.Annotation>() != null); + + OnDecompilationFinished(new DecompileEventArgs { + CodeMappings = builder.CodeMappings, + LocalVariables = builder.LocalVariables, + DecompiledMemberReferences = builder.DecompiledMemberReferences, + AstNodes = nodes + }); + } + + if (b is ReflectionDisassembler) { + var dis = b as ReflectionDisassembler; + OnDecompilationFinished(new DecompileEventArgs { + CodeMappings = dis.CodeMappings, + DecompiledMemberReferences = dis.DecompiledMemberReferences, + AstNodes = null // TODO: how can I find the nodes with line numbers from dis? + }); + } } } } diff --git a/ILSpy/Languages/Languages.cs b/ILSpy/Languages/Languages.cs new file mode 100644 index 000000000..5b9e18092 --- /dev/null +++ b/ILSpy/Languages/Languages.cs @@ -0,0 +1,60 @@ +// Copyright (c) 2011 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.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel.Composition.Hosting; +using System.Linq; + +namespace ICSharpCode.ILSpy +{ + public static class Languages + { + private static ReadOnlyCollection allLanguages; + + /// + /// A list of all languages. + /// + public static ReadOnlyCollection AllLanguages + { + get { return allLanguages; } + } + + internal static void Initialize(CompositionContainer composition) + { + List languages = new List(); + languages.AddRange(composition.GetExportedValues()); + languages.Add(new ILLanguage(true)); + #if DEBUG + languages.AddRange(ILAstLanguage.GetDebugLanguages()); + languages.AddRange(CSharpLanguage.GetDebugLanguages()); + #endif + allLanguages = languages.AsReadOnly(); + } + + /// + /// Gets a language using its name. + /// If the language is not found, C# is returned instead. + /// + public static Language GetLanguage(string name) + { + return AllLanguages.FirstOrDefault(l => l.Name == name) ?? AllLanguages.First(); + } + } +} diff --git a/ILSpy/LoadedAssembly.cs b/ILSpy/LoadedAssembly.cs index 8285be9ae..2bb0ae05d 100644 --- a/ILSpy/LoadedAssembly.cs +++ b/ILSpy/LoadedAssembly.cs @@ -171,6 +171,11 @@ namespace ICSharpCode.ILSpy } } + public IAssemblyResolver GetAssemblyResolver() + { + return new MyAssemblyResolver(this); + } + public LoadedAssembly LookupReferencedAssembly(string fullName) { foreach (LoadedAssembly asm in assemblyList.GetAssemblies()) { diff --git a/ILSpy/Properties/AssemblyInfo.template.cs b/ILSpy/Properties/AssemblyInfo.template.cs index b6c0c3fd9..247f8e857 100644 --- a/ILSpy/Properties/AssemblyInfo.template.cs +++ b/ILSpy/Properties/AssemblyInfo.template.cs @@ -31,7 +31,7 @@ using System.Runtime.InteropServices; internal static class RevisionClass { - public const string Major = "1"; + public const string Major = "2"; public const string Minor = "0"; public const string Build = "0"; public const string Revision = "$INSERTREVISION$"; diff --git a/ILSpy/SearchPane.cs b/ILSpy/SearchPane.cs index d935b424c..1b829ea71 100644 --- a/ILSpy/SearchPane.cs +++ b/ILSpy/SearchPane.cs @@ -35,14 +35,6 @@ using Mono.Cecil; namespace ICSharpCode.ILSpy { - /// - /// Notifies panes when they are closed. - /// - public interface IPane - { - void Closed(); - } - /// /// Search pane /// diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 024994462..a072f7311 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.ComponentModel.Composition; using System.Diagnostics; using System.Globalization; @@ -35,12 +36,16 @@ using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Threading; using System.Xml; + using ICSharpCode.AvalonEdit; +using ICSharpCode.AvalonEdit.Editing; using ICSharpCode.AvalonEdit.Folding; using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.AvalonEdit.Highlighting.Xshd; using ICSharpCode.AvalonEdit.Rendering; using ICSharpCode.Decompiler; +using ICSharpCode.ILSpy.AvalonEdit; +using ICSharpCode.ILSpy.Bookmarks; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.XmlDoc; using ICSharpCode.NRefactory.Documentation; @@ -55,7 +60,7 @@ namespace ICSharpCode.ILSpy.TextView /// Contains all the threading logic that makes the decompiler work in the background. /// [Export, PartCreationPolicy(CreationPolicy.Shared)] - public sealed partial class DecompilerTextView : UserControl + public sealed partial class DecompilerTextView : UserControl, IDisposable { readonly ReferenceElementGenerator referenceElementGenerator; readonly UIElementGenerator uiElementGenerator; @@ -66,6 +71,10 @@ namespace ICSharpCode.ILSpy.TextView DefinitionLookup definitionLookup; CancellationTokenSource currentCancellationTokenSource; + internal readonly IconBarManager manager; + readonly IconBarMargin iconMargin; + readonly TextMarkerService textMarkerService; + #region Constructor public DecompilerTextView() { @@ -79,6 +88,7 @@ namespace ICSharpCode.ILSpy.TextView } }); + this.Loaded+= new RoutedEventHandler(DecompilerTextView_Loaded); InitializeComponent(); this.referenceElementGenerator = new ReferenceElementGenerator(this.JumpToReference, this.IsLink); textEditor.TextArea.TextView.ElementGenerators.Add(referenceElementGenerator); @@ -89,8 +99,49 @@ namespace ICSharpCode.ILSpy.TextView textEditor.TextArea.TextView.MouseHoverStopped += TextViewMouseHoverStopped; textEditor.SetBinding(TextEditor.FontFamilyProperty, new Binding { Source = DisplaySettingsPanel.CurrentDisplaySettings, Path = new PropertyPath("SelectedFont") }); textEditor.SetBinding(TextEditor.FontSizeProperty, new Binding { Source = DisplaySettingsPanel.CurrentDisplaySettings, Path = new PropertyPath("SelectedFontSize") }); - textEditor.SetBinding(TextEditor.ShowLineNumbersProperty, new Binding { Source = DisplaySettingsPanel.CurrentDisplaySettings, Path = new PropertyPath("ShowLineNumbers") }); + + // add marker service & margin + iconMargin = new IconBarMargin((manager = new IconBarManager())); + textMarkerService = new TextMarkerService(); + textMarkerService.CodeEditor = textEditor; + textEditor.TextArea.TextView.BackgroundRenderers.Add(textMarkerService); + textEditor.TextArea.TextView.LineTransformers.Add(textMarkerService); + textEditor.ShowLineNumbers = true; + DisplaySettingsPanel.CurrentDisplaySettings.PropertyChanged += CurrentDisplaySettings_PropertyChanged; + + textEditor.TextArea.LeftMargins.Insert(0, iconMargin); + textEditor.TextArea.TextView.VisualLinesChanged += delegate { iconMargin.InvalidateVisual(); }; + ShowLineMargin(); + + // Bookmarks context menu + IconMarginActionsProvider.Add(iconMargin); + } + + void DecompilerTextView_Loaded(object sender, RoutedEventArgs e) + { + ShowLineMargin(); + } + + #endregion + + #region Line margin + + void CurrentDisplaySettings_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == "ShowLineNumbers") { + ShowLineMargin(); + } } + + void ShowLineMargin() + { + foreach (var margin in this.textEditor.TextArea.LeftMargins) { + if (margin is LineNumberMargin || margin is System.Windows.Shapes.Line) { + margin.Visibility = DisplaySettingsPanel.CurrentDisplaySettings.ShowLineNumbers ? Visibility.Visible : Visibility.Collapsed; + } + } + } + #endregion #region Tooltip support @@ -355,12 +406,14 @@ namespace ICSharpCode.ILSpy.TextView output.WriteLine(ex.ToString()); } ShowOutput(output); + } finally { + iconMargin.InvalidateVisual(); } decompiledNodes = context.TreeNodes; }); } - static Task DecompileAsync(DecompilationContext context, int outputLengthLimit) + Task DecompileAsync(DecompilationContext context, int outputLengthLimit) { Debug.WriteLine("Start decompilation of {0} tree nodes", context.TreeNodes.Length); @@ -405,9 +458,10 @@ namespace ICSharpCode.ILSpy.TextView return tcs.Task; } - static void DecompileNodes(DecompilationContext context, ITextOutput textOutput) + void DecompileNodes(DecompilationContext context, ITextOutput textOutput) { var nodes = context.TreeNodes; + context.Language.DecompileFinished += Language_DecompileFinished; for (int i = 0; i < nodes.Length; i++) { if (i > 0) textOutput.WriteLine(); @@ -415,6 +469,20 @@ namespace ICSharpCode.ILSpy.TextView context.Options.CancellationToken.ThrowIfCancellationRequested(); nodes[i].Decompile(context.Language, textOutput, context.Options); } + context.Language.DecompileFinished -= Language_DecompileFinished; + } + + void Language_DecompileFinished(object sender, DecompileEventArgs e) + { + if (e != null) { + manager.UpdateClassMemberBookmarks(e.AstNodes); + if (iconMargin.DecompiledMembers == null) { + iconMargin.DecompiledMembers = new List(); + } + iconMargin.DecompiledMembers.AddRange(e.DecompiledMemberReferences.Values.AsEnumerable()); + } else { + manager.UpdateClassMemberBookmarks(null); + } } #endregion @@ -604,6 +672,11 @@ namespace ICSharpCode.ILSpy.TextView state.DecompiledNodes = decompiledNodes; return state; } + + public void Dispose() + { + DisplaySettingsPanel.CurrentDisplaySettings.PropertyChanged -= CurrentDisplaySettings_PropertyChanged; + } } public class DecompilerTextViewState diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzeContextMenuEntry.cs b/ILSpy/TreeNodes/Analyzer/AnalyzeContextMenuEntry.cs index 6221bcc37..1c790ebb2 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzeContextMenuEntry.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzeContextMenuEntry.cs @@ -35,10 +35,10 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer { foreach (IMemberTreeNode node in selectedNodes) { if (!(node.Member is TypeDefinition - || node.Member is FieldDefinition - || node.Member is MethodDefinition - || Analyzer.AnalyzedPropertyTreeNode.CanShow(node.Member) - || Analyzer.AnalyzedEventTreeNode.CanShow(node.Member))) + || node.Member is FieldDefinition + || node.Member is MethodDefinition + || Analyzer.AnalyzedPropertyTreeNode.CanShow(node.Member) + || Analyzer.AnalyzedEventTreeNode.CanShow(node.Member))) return false; } @@ -50,22 +50,27 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer // TODO: figure out when equivalent nodes are already present // and focus those instead. foreach (IMemberTreeNode node in selectedNodes) { - TypeDefinition type = node.Member as TypeDefinition; - if (type != null) - AnalyzerTreeView.Instance.Show(new AnalyzedTypeTreeNode(type)); - FieldDefinition field = node.Member as FieldDefinition; - if (field != null) - AnalyzerTreeView.Instance.Show(new AnalyzedFieldTreeNode(field)); - MethodDefinition method = node.Member as MethodDefinition; - if (method != null) - AnalyzerTreeView.Instance.Show(new AnalyzedMethodTreeNode(method)); - var propertyAnalyzer = Analyzer.AnalyzedPropertyTreeNode.TryCreateAnalyzer(node.Member); - if (propertyAnalyzer != null) - AnalyzerTreeView.Instance.Show(propertyAnalyzer); - var eventAnalyzer = Analyzer.AnalyzedEventTreeNode.TryCreateAnalyzer(node.Member); - if (eventAnalyzer != null) - AnalyzerTreeView.Instance.Show(eventAnalyzer); + Analyze(node.Member); } } + + public static void Analyze(MemberReference member) + { + TypeDefinition type = member as TypeDefinition; + if (type != null) + AnalyzerTreeView.Instance.Show(new AnalyzedTypeTreeNode(type)); + FieldDefinition field = member as FieldDefinition; + if (field != null) + AnalyzerTreeView.Instance.Show(new AnalyzedFieldTreeNode(field)); + MethodDefinition method = member as MethodDefinition; + if (method != null) + AnalyzerTreeView.Instance.Show(new AnalyzedMethodTreeNode(method)); + var propertyAnalyzer = Analyzer.AnalyzedPropertyTreeNode.TryCreateAnalyzer(member); + if (propertyAnalyzer != null) + AnalyzerTreeView.Instance.Show(propertyAnalyzer); + var eventAnalyzer = Analyzer.AnalyzedEventTreeNode.TryCreateAnalyzer(member); + if (eventAnalyzer != null) + AnalyzerTreeView.Instance.Show(eventAnalyzer); + } } } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedEventFiredByTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedEventFiredByTreeNode.cs index 02320cc09..f0247dc55 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedEventFiredByTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedEventFiredByTreeNode.cs @@ -77,7 +77,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer { foundMethods = new ConcurrentDictionary(); - foreach (var child in FindReferencesInType(analyzedEvent.DeclaringType)) { + foreach (var child in FindReferencesInType(analyzedEvent.DeclaringType).OrderBy(n => n.Text)) { yield return child; } @@ -117,7 +117,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer if (found) { MethodDefinition codeLocation = this.Language.GetOriginalCodeLocation(method) as MethodDefinition; if (codeLocation != null && !HasAlreadyBeenFound(codeLocation)) { - yield return new AnalyzedMethodTreeNode(codeLocation); + var node = new AnalyzedMethodTreeNode(codeLocation); + node.Language = this.Language; + yield return node; } } } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedEventOverridesTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedEventOverridesTreeNode.cs index 549152bb7..99aa27af6 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedEventOverridesTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedEventOverridesTreeNode.cs @@ -71,7 +71,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer ScopedWhereUsedAnalyzer analyzer; analyzer = new ScopedWhereUsedAnalyzer(analyzedEvent, FindReferencesInType); - return analyzer.PerformAnalysis(ct); + foreach (var child in analyzer.PerformAnalysis(ct).OrderBy(n => n.Text)) { + yield return child; + } } private IEnumerable FindReferencesInType(TypeDefinition type) @@ -86,7 +88,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer if (TypesHierarchyHelpers.IsBaseEvent(analyzedEvent, eventDef)) { MethodDefinition anyAccessor = eventDef.AddMethod ?? eventDef.RemoveMethod; bool hidesParent = !anyAccessor.IsVirtual ^ anyAccessor.IsNewSlot; - yield return new AnalyzedEventTreeNode(eventDef, hidesParent ? "(hides) " : ""); + var node = new AnalyzedEventTreeNode(eventDef, hidesParent ? "(hides) " : ""); + node.Language = this.Language; + yield return node; } } } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedFieldAccessTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedFieldAccessTreeNode.cs index 850ab3141..edc14fd04 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedFieldAccessTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedFieldAccessTreeNode.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading; using ICSharpCode.TreeView; using Mono.Cecil; @@ -74,7 +75,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer foundMethods = new Lazy(LazyThreadSafetyMode.ExecutionAndPublication); var analyzer = new ScopedWhereUsedAnalyzer(analyzedField, FindReferencesInType); - foreach (var child in analyzer.PerformAnalysis(ct)) { + foreach (var child in analyzer.PerformAnalysis(ct).OrderBy(n => n.Text)) { yield return child; } @@ -93,8 +94,8 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer foreach (Instruction instr in method.Body.Instructions) { if (CanBeReference(instr.OpCode.Code)) { FieldReference fr = instr.Operand as FieldReference; - if (fr != null && fr.Name == name && - Helpers.IsReferencedBy(analyzedField.DeclaringType, fr.DeclaringType) && + if (fr != null && fr.Name == name && + Helpers.IsReferencedBy(analyzedField.DeclaringType, fr.DeclaringType) && fr.Resolve() == analyzedField) { found = true; break; @@ -107,7 +108,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer if (found) { MethodDefinition codeLocation = this.Language.GetOriginalCodeLocation(method) as MethodDefinition; if (codeLocation != null && !HasAlreadyBeenFound(codeLocation)) { - yield return new AnalyzedMethodTreeNode(codeLocation); + var node = new AnalyzedMethodTreeNode(codeLocation); + node.Language = this.Language; + yield return node; } } } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedInterfaceEventImplementedByTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedInterfaceEventImplementedByTreeNode.cs index 09cab0bf3..f6773be82 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedInterfaceEventImplementedByTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedInterfaceEventImplementedByTreeNode.cs @@ -71,7 +71,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer { ScopedWhereUsedAnalyzer analyzer; analyzer = new ScopedWhereUsedAnalyzer(analyzedMethod, FindReferencesInType); - return analyzer.PerformAnalysis(ct); + foreach (var child in analyzer.PerformAnalysis(ct).OrderBy(n => n.Text)) { + yield return child; + } } private IEnumerable FindReferencesInType(TypeDefinition type) @@ -84,15 +86,20 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer foreach (EventDefinition ev in type.Events.Where(e => e.Name == analyzedEvent.Name)) { MethodDefinition accessor = ev.AddMethod ?? ev.RemoveMethod; - if (TypesHierarchyHelpers.MatchInterfaceMethod(accessor, analyzedMethod, implementedInterfaceRef)) - yield return new AnalyzedEventTreeNode(ev); + if (TypesHierarchyHelpers.MatchInterfaceMethod(accessor, analyzedMethod, implementedInterfaceRef)) { + var node = new AnalyzedEventTreeNode(ev); + node.Language = this.Language; + yield return node; + } yield break; } foreach (EventDefinition ev in type.Events.Where(e => e.Name.EndsWith(analyzedEvent.Name))) { MethodDefinition accessor = ev.AddMethod ?? ev.RemoveMethod; if (accessor.HasOverrides && accessor.Overrides.Any(m => m.Resolve() == analyzedMethod)) { - yield return new AnalyzedEventTreeNode(ev); + var node = new AnalyzedEventTreeNode(ev); + node.Language = this.Language; + yield return node; } } } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedInterfaceMethodImplementedByTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedInterfaceMethodImplementedByTreeNode.cs index 960f48f89..3f139e040 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedInterfaceMethodImplementedByTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedInterfaceMethodImplementedByTreeNode.cs @@ -69,7 +69,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer { ScopedWhereUsedAnalyzer analyzer; analyzer = new ScopedWhereUsedAnalyzer(analyzedMethod, FindReferencesInType); - return analyzer.PerformAnalysis(ct); + foreach (var child in analyzer.PerformAnalysis(ct).OrderBy(n => n.Text)) { + yield return child; + } } private IEnumerable FindReferencesInType(TypeDefinition type) @@ -81,14 +83,19 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer yield break; foreach (MethodDefinition method in type.Methods.Where(m => m.Name == analyzedMethod.Name)) { - if (TypesHierarchyHelpers.MatchInterfaceMethod(method, analyzedMethod, implementedInterfaceRef)) - yield return new AnalyzedMethodTreeNode(method); + if (TypesHierarchyHelpers.MatchInterfaceMethod(method, analyzedMethod, implementedInterfaceRef)) { + var node = new AnalyzedMethodTreeNode(method); + node.Language = this.Language; + yield return node; + } yield break; } foreach (MethodDefinition method in type.Methods) { if (method.HasOverrides && method.Overrides.Any(m => m.Resolve() == analyzedMethod)) { - yield return new AnalyzedMethodTreeNode(method); + var node = new AnalyzedMethodTreeNode(method); + node.Language = this.Language; + yield return node; } } } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedInterfacePropertyImplementedByTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedInterfacePropertyImplementedByTreeNode.cs index 35d9bc3d0..56f89ad13 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedInterfacePropertyImplementedByTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedInterfacePropertyImplementedByTreeNode.cs @@ -71,7 +71,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer { ScopedWhereUsedAnalyzer analyzer; analyzer = new ScopedWhereUsedAnalyzer(analyzedMethod, FindReferencesInType); - return analyzer.PerformAnalysis(ct); + foreach (var child in analyzer.PerformAnalysis(ct).OrderBy(n => n.Text)) { + yield return child; + } } private IEnumerable FindReferencesInType(TypeDefinition type) @@ -84,15 +86,20 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer foreach (PropertyDefinition property in type.Properties.Where(e => e.Name == analyzedProperty.Name)) { MethodDefinition accessor = property.GetMethod ?? property.SetMethod; - if (TypesHierarchyHelpers.MatchInterfaceMethod(accessor, analyzedMethod, implementedInterfaceRef)) - yield return new AnalyzedPropertyTreeNode(property); + if (TypesHierarchyHelpers.MatchInterfaceMethod(accessor, analyzedMethod, implementedInterfaceRef)) { + var node = new AnalyzedPropertyTreeNode(property); + node.Language = this.Language; + yield return node; + } yield break; } foreach (PropertyDefinition property in type.Properties.Where(e => e.Name.EndsWith(analyzedProperty.Name))) { MethodDefinition accessor = property.GetMethod ?? property.SetMethod; if (accessor.HasOverrides && accessor.Overrides.Any(m => m.Resolve() == analyzedMethod)) { - yield return new AnalyzedPropertyTreeNode(property); + var node = new AnalyzedPropertyTreeNode(property); + node.Language = this.Language; + yield return node; } } } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedMethodOverridesTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedMethodOverridesTreeNode.cs index 05a3f5d6a..4e8af7663 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedMethodOverridesTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedMethodOverridesTreeNode.cs @@ -76,12 +76,14 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer ScopedWhereUsedAnalyzer analyzer; analyzer = new ScopedWhereUsedAnalyzer(analyzedMethod, FindReferencesInType); - return analyzer.PerformAnalysis(ct); + foreach (var child in analyzer.PerformAnalysis(ct).OrderBy(n => n.Text)) { + yield return child; + } } private IEnumerable FindReferencesInType(TypeDefinition type) { - SharpTreeNode newNode = null; + AnalyzerTreeNode newNode = null; try { if (!TypesHierarchyHelpers.IsBaseType(analyzedMethod.DeclaringType, type, resolveTypeArguments: false)) yield break; @@ -97,8 +99,10 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer // ignore this type definition. maybe add a notification about such cases. } - if (newNode != null) + if (newNode != null) { + newNode.Language = this.Language; yield return newNode; + } } public static bool CanShow(MethodDefinition method) diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs index f3f9504e6..175ce4efc 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Linq; using System.Threading; using ICSharpCode.TreeView; using Mono.Cecil; @@ -71,7 +72,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer foundMethods = new ConcurrentDictionary(); var analyzer = new ScopedWhereUsedAnalyzer(analyzedMethod, FindReferencesInType); - foreach (var child in analyzer.PerformAnalysis(ct)) { + foreach (var child in analyzer.PerformAnalysis(ct).OrderBy(n => n.Text)) { yield return child; } @@ -100,7 +101,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer if (found) { MethodDefinition codeLocation = this.Language.GetOriginalCodeLocation(method) as MethodDefinition; if (codeLocation != null && !HasAlreadyBeenFound(codeLocation)) { - yield return new AnalyzedMethodTreeNode(codeLocation); + var node= new AnalyzedMethodTreeNode(codeLocation); + node.Language = this.Language; + yield return node; } } } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsesTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsesTreeNode.cs index 5553f5a98..1b18a9ae1 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsesTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsesTreeNode.cs @@ -51,14 +51,25 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer } protected override void LoadChildren() + { + analyzedMethod.Body = null; + foreach (var child in GetChildren().OrderBy(n => n.Text)) { + this.Children.Add(child); + } + } + + private IEnumerable GetChildren() { foreach (var f in GetUsedFields().Distinct()) { - this.Children.Add(new AnalyzedFieldTreeNode(f)); + var node = new AnalyzedFieldTreeNode(f); + node.Language = this.Language; + yield return node; } foreach (var m in GetUsedMethods().Distinct()) { - this.Children.Add(new AnalyzedMethodTreeNode(m)); + var node = new AnalyzedMethodTreeNode(m); + node.Language = this.Language; + yield return node; } - analyzedMethod.Body = null; } private IEnumerable GetUsedMethods() diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedPropertyOverridesTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedPropertyOverridesTreeNode.cs index 6a6ea0f32..8f264d7ff 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedPropertyOverridesTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedPropertyOverridesTreeNode.cs @@ -72,7 +72,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer ScopedWhereUsedAnalyzer analyzer; analyzer = new ScopedWhereUsedAnalyzer(analyzedProperty, FindReferencesInType); - return analyzer.PerformAnalysis(ct); + foreach (var child in analyzer.PerformAnalysis(ct).OrderBy(n => n.Text)) { + yield return child; + } } private IEnumerable FindReferencesInType(TypeDefinition type) @@ -88,7 +90,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer if (TypesHierarchyHelpers.IsBaseProperty(analyzedProperty, property)) { MethodDefinition anyAccessor = property.GetMethod ?? property.SetMethod; bool hidesParent = !anyAccessor.IsVirtual ^ anyAccessor.IsNewSlot; - yield return new AnalyzedPropertyTreeNode(property, hidesParent ? "(hides) " : ""); + var node = new AnalyzedPropertyTreeNode(property, hidesParent ? "(hides) " : ""); + node.Language = this.Language; + yield return node; } } } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedTypeExposedByTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedTypeExposedByTreeNode.cs index cd14c0575..26c2bca71 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedTypeExposedByTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedTypeExposedByTreeNode.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading; using ICSharpCode.TreeView; using Mono.Cecil; @@ -68,7 +69,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer ScopedWhereUsedAnalyzer analyzer; analyzer = new ScopedWhereUsedAnalyzer(analyzedType, FindReferencesInType); - return analyzer.PerformAnalysis(ct); + foreach (var child in analyzer.PerformAnalysis(ct).OrderBy(n => n.Text)) { + yield return child; + } } private IEnumerable FindReferencesInType(TypeDefinition type) @@ -80,23 +83,35 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer yield break; foreach (FieldDefinition field in type.Fields) { - if (TypeIsExposedBy(field)) - yield return new AnalyzedFieldTreeNode(field); + if (TypeIsExposedBy(field)) { + var node = new AnalyzedFieldTreeNode(field); + node.Language = this.Language; + yield return node; + } } foreach (PropertyDefinition property in type.Properties) { - if (TypeIsExposedBy(property)) - yield return new AnalyzedPropertyTreeNode(property); + if (TypeIsExposedBy(property)) { + var node = new AnalyzedPropertyTreeNode(property); + node.Language = this.Language; + yield return node; + } } foreach (EventDefinition eventDef in type.Events) { - if (TypeIsExposedBy(eventDef)) - yield return new AnalyzedEventTreeNode(eventDef); + if (TypeIsExposedBy(eventDef)) { + var node = new AnalyzedEventTreeNode(eventDef); + node.Language = this.Language; + yield return node; + } } foreach (MethodDefinition method in type.Methods) { - if (TypeIsExposedBy(method)) - yield return new AnalyzedMethodTreeNode(method); + if (TypeIsExposedBy(method)) { + var node = new AnalyzedMethodTreeNode(method); + node.Language = this.Language; + yield return node; + } } } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedTypeExtensionMethodsTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedTypeExtensionMethodsTreeNode.cs index 3bd42506c..605190b88 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedTypeExtensionMethodsTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedTypeExtensionMethodsTreeNode.cs @@ -69,7 +69,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer ScopedWhereUsedAnalyzer analyzer; analyzer = new ScopedWhereUsedAnalyzer(analyzedType, FindReferencesInType); - return analyzer.PerformAnalysis(ct); + foreach (var child in analyzer.PerformAnalysis(ct).OrderBy(n => n.Text)) { + yield return child; + } } private IEnumerable FindReferencesInType(TypeDefinition type) @@ -79,7 +81,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer foreach (MethodDefinition method in type.Methods) { if (method.IsStatic && HasExtensionAttribute(method)) { if (method.HasParameters && method.Parameters[0].ParameterType.Resolve() == analyzedType) { - yield return new AnalyzedMethodTreeNode(method); + var node = new AnalyzedMethodTreeNode(method); + node.Language = this.Language; + yield return node; } } } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedTypeInstantiationsTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedTypeInstantiationsTreeNode.cs index 0c2d9465a..10b477bcf 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedTypeInstantiationsTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedTypeInstantiationsTreeNode.cs @@ -74,7 +74,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer ScopedWhereUsedAnalyzer analyzer; analyzer = new ScopedWhereUsedAnalyzer(analyzedType, FindReferencesInType); - return analyzer.PerformAnalysis(ct); + foreach (var child in analyzer.PerformAnalysis(ct).OrderBy(n => n.Text)) { + yield return child; + } } private IEnumerable FindReferencesInType(TypeDefinition type) @@ -102,8 +104,11 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer method.Body = null; - if (found) - yield return new AnalyzedMethodTreeNode(method); + if (found) { + var node = new AnalyzedMethodTreeNode(method); + node.Language = this.Language; + yield return node; + } } } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedVirtualMethodUsedByTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedVirtualMethodUsedByTreeNode.cs index 26f5e4c65..f31eb5d5e 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedVirtualMethodUsedByTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedVirtualMethodUsedByTreeNode.cs @@ -75,7 +75,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer InitializeAnalyzer(); var analyzer = new ScopedWhereUsedAnalyzer(analyzedMethod, FindReferencesInType); - foreach (var child in analyzer.PerformAnalysis(ct)) { + foreach (var child in analyzer.PerformAnalysis(ct).OrderBy(n => n.Text)) { yield return child; } @@ -95,8 +95,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer possibleTypes = new List(); TypeReference type = analyzedMethod.DeclaringType.BaseType; - while (type !=null) - { + while (type != null) { possibleTypes.Add(type); type = type.Resolve().BaseType; } @@ -120,8 +119,8 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer MethodReference mr = instr.Operand as MethodReference; if (mr != null && mr.Name == name) { // explicit call to the requested method - if (instr.OpCode.Code == Code.Call - && Helpers.IsReferencedBy(analyzedMethod.DeclaringType, mr.DeclaringType) + if (instr.OpCode.Code == Code.Call + && Helpers.IsReferencedBy(analyzedMethod.DeclaringType, mr.DeclaringType) && mr.Resolve() == analyzedMethod) { found = true; prefix = "(as base) "; @@ -147,7 +146,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer if (found) { MethodDefinition codeLocation = this.Language.GetOriginalCodeLocation(method) as MethodDefinition; if (codeLocation != null && !HasAlreadyBeenFound(codeLocation)) { - yield return new AnalyzedMethodTreeNode(codeLocation, prefix); + var node = new AnalyzedMethodTreeNode(codeLocation); + node.Language = this.Language; + yield return node; } } } diff --git a/ILSpy/TreeNodes/BaseTypesEntryNode.cs b/ILSpy/TreeNodes/BaseTypesEntryNode.cs new file mode 100644 index 000000000..821784ccb --- /dev/null +++ b/ILSpy/TreeNodes/BaseTypesEntryNode.cs @@ -0,0 +1,104 @@ +// Copyright (c) 2011 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.Linq; +using ICSharpCode.Decompiler; +using ICSharpCode.TreeView; +using Mono.Cecil; + +namespace ICSharpCode.ILSpy.TreeNodes +{ + sealed class BaseTypesEntryNode : ILSpyTreeNode, IMemberTreeNode + { + private TypeReference tr; + private TypeDefinition def; + private bool isInterface; + + public BaseTypesEntryNode(TypeReference tr, bool isInterface) + { + if (tr == null) + throw new ArgumentNullException("tr"); + this.tr = tr; + this.def = tr.Resolve(); + this.isInterface = isInterface; + this.LazyLoading = true; + } + + public override bool ShowExpander + { + get { return def != null && (def.BaseType != null || def.HasInterfaces); } + } + + public override object Text + { + get { return this.Language.TypeToString(tr, true); } + } + + public override object Icon + { + get + { + if (def != null) + return TypeTreeNode.GetIcon(def); + else + return isInterface ? Images.Interface : Images.Class; + } + } + + protected override void LoadChildren() + { + if (def != null) + BaseTypesTreeNode.AddBaseTypes(this.Children, def); + } + + public override void ActivateItem(System.Windows.RoutedEventArgs e) + { + // on item activation, try to resolve once again (maybe the user loaded the assembly in the meantime) + if (def == null) { + def = tr.Resolve(); + if (def != null) + this.LazyLoading = true; + // re-load children + } + e.Handled = ActivateItem(this, def); + } + + internal static bool ActivateItem(SharpTreeNode node, TypeDefinition def) + { + if (def != null) { + var assemblyListNode = node.Ancestors().OfType().FirstOrDefault(); + if (assemblyListNode != null) { + assemblyListNode.Select(assemblyListNode.FindTypeNode(def)); + return true; + } + } + return false; + } + + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + { + language.WriteCommentLine(output, language.TypeToString(tr, true)); + } + + MemberReference IMemberTreeNode.Member + { + get { return tr; } + } + } +} diff --git a/ILSpy/TreeNodes/BaseTypesTreeNode.cs b/ILSpy/TreeNodes/BaseTypesTreeNode.cs index 0209f7410..610458cdf 100644 --- a/ILSpy/TreeNodes/BaseTypesTreeNode.cs +++ b/ILSpy/TreeNodes/BaseTypesTreeNode.cs @@ -17,9 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System; -using System.Linq; using System.Windows.Threading; - using ICSharpCode.Decompiler; using ICSharpCode.TreeView; using Mono.Cecil; @@ -32,26 +30,28 @@ namespace ICSharpCode.ILSpy.TreeNodes sealed class BaseTypesTreeNode : ILSpyTreeNode { readonly TypeDefinition type; - + public BaseTypesTreeNode(TypeDefinition type) { this.type = type; this.LazyLoading = true; } - - public override object Text { + + public override object Text + { get { return "Base Types"; } } - - public override object Icon { + + public override object Icon + { get { return Images.SuperTypes; } } - + protected override void LoadChildren() { AddBaseTypes(this.Children, type); } - + internal static void AddBaseTypes(SharpTreeNodeCollection children, TypeDefinition type) { if (type.BaseType != null) @@ -60,7 +60,7 @@ namespace ICSharpCode.ILSpy.TreeNodes children.Add(new BaseTypesEntryNode(i, true)); } } - + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(EnsureLazyChildren)); @@ -69,78 +69,4 @@ namespace ICSharpCode.ILSpy.TreeNodes } } } - - sealed class BaseTypesEntryNode : ILSpyTreeNode, IMemberTreeNode - { - TypeReference tr; - TypeDefinition def; - bool isInterface; - - public BaseTypesEntryNode(TypeReference tr, bool isInterface) - { - if (tr == null) - throw new ArgumentNullException("tr"); - this.tr = tr; - this.def = tr.Resolve(); - this.isInterface = isInterface; - this.LazyLoading = true; - } - - public override bool ShowExpander { - get { - return def != null && (def.BaseType != null || def.HasInterfaces); - } - } - - public override object Text { - get { return this.Language.TypeToString(tr, true); } - } - - public override object Icon { - get { - if (def != null) - return TypeTreeNode.GetIcon(def); - else - return isInterface ? Images.Interface : Images.Class; - } - } - - protected override void LoadChildren() - { - if (def != null) - BaseTypesTreeNode.AddBaseTypes(this.Children, def); - } - - public override void ActivateItem(System.Windows.RoutedEventArgs e) - { - // on item activation, try to resolve once again (maybe the user loaded the assembly in the meantime) - if (def == null) { - def = tr.Resolve(); - if (def != null) - this.LazyLoading = true; // re-load children - } - e.Handled = ActivateItem(this, def); - } - - internal static bool ActivateItem(SharpTreeNode node, TypeDefinition def) - { - if (def != null) { - var assemblyListNode = node.Ancestors().OfType().FirstOrDefault(); - if (assemblyListNode != null) { - assemblyListNode.Select(assemblyListNode.FindTypeNode(def)); - return true; - } - } - return false; - } - - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) - { - language.WriteCommentLine(output, language.TypeToString(tr, true)); - } - - MemberReference IMemberTreeNode.Member { - get { return tr; } - } - } -} +} \ No newline at end of file diff --git a/ILSpy/TreeNodes/DerivedTypesEntryNode.cs b/ILSpy/TreeNodes/DerivedTypesEntryNode.cs new file mode 100644 index 000000000..326418633 --- /dev/null +++ b/ILSpy/TreeNodes/DerivedTypesEntryNode.cs @@ -0,0 +1,111 @@ +// Copyright (c) 2011 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.Collections.Generic; +using System.Threading; +using ICSharpCode.Decompiler; +using Mono.Cecil; + +namespace ICSharpCode.ILSpy.TreeNodes +{ + class DerivedTypesEntryNode : ILSpyTreeNode, IMemberTreeNode + { + private TypeDefinition type; + private AssemblyDefinition[] assemblies; + private ThreadingSupport threading; + + public DerivedTypesEntryNode(TypeDefinition type, AssemblyDefinition[] assemblies) + { + this.type = type; + this.assemblies = assemblies; + this.LazyLoading = true; + threading = new ThreadingSupport(); + } + + public override bool ShowExpander + { + get { return !type.IsSealed && base.ShowExpander; } + } + + public override object Text + { + get { return this.Language.TypeToString(type, true); } + } + + public override object Icon + { + get { return TypeTreeNode.GetIcon(type); } + } + + public override FilterResult Filter(FilterSettings settings) + { + if (!settings.ShowInternalApi && !IsPublicAPI) + return FilterResult.Hidden; + if (settings.SearchTermMatches(type.Name)) { + if (type.IsNested && !settings.Language.ShowMember(type)) + return FilterResult.Hidden; + else + return FilterResult.Match; + } else + return FilterResult.Recurse; + } + + public bool IsPublicAPI + { + get + { + switch (type.Attributes & TypeAttributes.VisibilityMask) { + case TypeAttributes.Public: + case TypeAttributes.NestedPublic: + case TypeAttributes.NestedFamily: + case TypeAttributes.NestedFamORAssem: + return true; + default: + return false; + } + } + } + + protected override void LoadChildren() + { + threading.LoadChildren(this, FetchChildren); + } + + IEnumerable FetchChildren(CancellationToken ct) + { + // FetchChildren() runs on the main thread; but the enumerator will be consumed on a background thread + return DerivedTypesTreeNode.FindDerivedTypes(type, assemblies, ct); + } + + public override void ActivateItem(System.Windows.RoutedEventArgs e) + { + e.Handled = BaseTypesEntryNode.ActivateItem(this, type); + } + + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + { + language.WriteCommentLine(output, language.TypeToString(type, true)); + } + + MemberReference IMemberTreeNode.Member + { + get { return type; } + } + } +} diff --git a/ILSpy/TreeNodes/DerivedTypesTreeNode.cs b/ILSpy/TreeNodes/DerivedTypesTreeNode.cs index 2fd497395..0775f58e9 100644 --- a/ILSpy/TreeNodes/DerivedTypesTreeNode.cs +++ b/ILSpy/TreeNodes/DerivedTypesTreeNode.cs @@ -20,7 +20,6 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading; - using ICSharpCode.Decompiler; using ICSharpCode.NRefactory.Utils; using Mono.Cecil; @@ -35,7 +34,7 @@ namespace ICSharpCode.ILSpy.TreeNodes readonly AssemblyList list; readonly TypeDefinition type; ThreadingSupport threading; - + public DerivedTypesTreeNode(AssemblyList list, TypeDefinition type) { this.list = list; @@ -43,27 +42,29 @@ namespace ICSharpCode.ILSpy.TreeNodes this.LazyLoading = true; this.threading = new ThreadingSupport(); } - - public override object Text { + + public override object Text + { get { return "Derived Types"; } } - - public override object Icon { + + public override object Icon + { get { return Images.SubTypes; } } - + protected override void LoadChildren() { threading.LoadChildren(this, FetchChildren); } - + IEnumerable FetchChildren(CancellationToken cancellationToken) { // FetchChildren() runs on the main thread; but the enumerator will be consumed on a background thread var assemblies = list.GetAssemblies().Select(node => node.AssemblyDefinition).Where(asm => asm != null).ToArray(); return FindDerivedTypes(type, assemblies, cancellationToken); } - + internal static IEnumerable FindDerivedTypes(TypeDefinition type, AssemblyDefinition[] assemblies, CancellationToken cancellationToken) { foreach (AssemblyDefinition asm in assemblies) { @@ -80,7 +81,7 @@ namespace ICSharpCode.ILSpy.TreeNodes } } } - + static bool IsSameType(TypeReference typeRef, TypeDefinition type) { if (typeRef.FullName == type.FullName) @@ -96,66 +97,10 @@ namespace ICSharpCode.ILSpy.TreeNodes return false; return true; } - + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { threading.Decompile(language, output, options, EnsureLazyChildren); } } - - class DerivedTypesEntryNode : ILSpyTreeNode, IMemberTreeNode - { - TypeDefinition def; - AssemblyDefinition[] assemblies; - ThreadingSupport threading; - - public DerivedTypesEntryNode(TypeDefinition def, AssemblyDefinition[] assemblies) - { - this.def = def; - this.assemblies = assemblies; - this.LazyLoading = true; - threading = new ThreadingSupport(); - } - - public override bool ShowExpander { - get { - return !def.IsSealed && base.ShowExpander; - } - } - - public override object Text { - get { return this.Language.TypeToString(def, true); } - } - - public override object Icon { - get { - return TypeTreeNode.GetIcon(def); - } - } - - protected override void LoadChildren() - { - threading.LoadChildren(this, FetchChildren); - } - - IEnumerable FetchChildren(CancellationToken ct) - { - // FetchChildren() runs on the main thread; but the enumerator will be consumed on a background thread - return DerivedTypesTreeNode.FindDerivedTypes(def, assemblies, ct); - } - - public override void ActivateItem(System.Windows.RoutedEventArgs e) - { - e.Handled = BaseTypesEntryNode.ActivateItem(this, def); - } - - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) - { - language.WriteCommentLine(output, language.TypeToString(def, true)); - } - - MemberReference IMemberTreeNode.Member { - get { return def; } - } - } -} +} \ No newline at end of file diff --git a/ILSpy/TreeNodes/FilterResult.cs b/ILSpy/TreeNodes/FilterResult.cs new file mode 100644 index 000000000..106bed1e3 --- /dev/null +++ b/ILSpy/TreeNodes/FilterResult.cs @@ -0,0 +1,40 @@ +// Copyright (c) 2011 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. + +namespace ICSharpCode.ILSpy.TreeNodes +{ + public enum FilterResult + { + /// + /// Hides the node. + /// + Hidden, + /// + /// Shows the node (and resets the search term for child nodes). + /// + Match, + /// + /// Hides the node only if all children are hidden (and resets the search term for child nodes). + /// + MatchAndRecurse, + /// + /// Hides the node only if all children are hidden (doesn't reset the search term for child nodes). + /// + Recurse + } +} diff --git a/ILSpy/TreeNodes/ILSpyTreeNode.cs b/ILSpy/TreeNodes/ILSpyTreeNode.cs index ea92d8f22..8b4835fe5 100644 --- a/ILSpy/TreeNodes/ILSpyTreeNode.cs +++ b/ILSpy/TreeNodes/ILSpyTreeNode.cs @@ -17,7 +17,6 @@ // DEALINGS IN THE SOFTWARE. using System; -using System.Collections.ObjectModel; using System.Collections.Specialized; using System.ComponentModel; using System.Linq; @@ -33,21 +32,24 @@ namespace ICSharpCode.ILSpy.TreeNodes { FilterSettings filterSettings; bool childrenNeedFiltering; - - public FilterSettings FilterSettings { + + public FilterSettings FilterSettings + { get { return filterSettings; } - set { + set + { if (filterSettings != value) { filterSettings = value; OnFilterSettingsChanged(); } } } - - public Language Language { + + public Language Language + { get { return filterSettings != null ? filterSettings.Language : Languages.AllLanguages[0]; } } - + public virtual FilterResult Filter(FilterSettings settings) { if (string.IsNullOrEmpty(settings.SearchTerm)) @@ -55,15 +57,15 @@ namespace ICSharpCode.ILSpy.TreeNodes else return FilterResult.Hidden; } - + protected static object HighlightSearchMatch(string text, string suffix = null) { // TODO: implement highlighting the search match return text + suffix; } - + public abstract void Decompile(Language language, ITextOutput output, DecompilationOptions options); - + /// /// Used to implement special view logic for some items. /// This method is called on the main thread when only a single item is selected. @@ -73,7 +75,7 @@ namespace ICSharpCode.ILSpy.TreeNodes { return false; } - + /// /// Used to implement special save logic for some items. /// This method is called on the main thread when only a single item is selected. @@ -83,7 +85,7 @@ namespace ICSharpCode.ILSpy.TreeNodes { return false; } - + protected override void OnChildrenChanged(NotifyCollectionChangedEventArgs e) { if (e.NewItems != null) { @@ -96,7 +98,7 @@ namespace ICSharpCode.ILSpy.TreeNodes } base.OnChildrenChanged(e); } - + void ApplyFilterToChild(ILSpyTreeNode child) { FilterResult r; @@ -126,7 +128,7 @@ namespace ICSharpCode.ILSpy.TreeNodes throw new InvalidEnumArgumentException(); } } - + FilterSettings StripSearchTerm(FilterSettings filterSettings) { if (filterSettings == null) @@ -137,7 +139,7 @@ namespace ICSharpCode.ILSpy.TreeNodes } return filterSettings; } - + protected virtual void OnFilterSettingsChanged() { RaisePropertyChanged("Text"); @@ -148,13 +150,13 @@ namespace ICSharpCode.ILSpy.TreeNodes childrenNeedFiltering = true; } } - + protected override void OnIsVisibleChanged() { base.OnIsVisibleChanged(); EnsureChildrenFiltered(); } - + void EnsureChildrenFiltered() { EnsureLazyChildren(); @@ -165,24 +167,4 @@ namespace ICSharpCode.ILSpy.TreeNodes } } } - - public enum FilterResult - { - /// - /// Hides the node. - /// - Hidden, - /// - /// Shows the node (and resets the search term for child nodes). - /// - Match, - /// - /// Hides the node only if all children are hidden (and resets the search term for child nodes). - /// - MatchAndRecurse, - /// - /// Hides the node only if all children are hidden (doesn't reset the search term for child nodes). - /// - Recurse - } -} +} \ No newline at end of file diff --git a/ILSpy/TreeNodes/ResourceNodes/CursorResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceNodes/CursorResourceEntryNode.cs new file mode 100644 index 000000000..3170f0992 --- /dev/null +++ b/ILSpy/TreeNodes/ResourceNodes/CursorResourceEntryNode.cs @@ -0,0 +1,95 @@ +// Copyright (c) 2011 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.ComponentModel.Composition; +using System.IO; +using System.Windows.Controls; +using System.Windows.Media.Imaging; +using ICSharpCode.ILSpy.TextView; +using Mono.Cecil; + +namespace ICSharpCode.ILSpy.TreeNodes +{ + [Export(typeof(IResourceNodeFactory))] + sealed class CursorResourceNodeFactory : IResourceNodeFactory + { + static readonly string[] imageFileExtensions = { ".cur" }; + + public ILSpyTreeNode CreateNode(Mono.Cecil.Resource resource) + { + EmbeddedResource er = resource as EmbeddedResource; + if (er != null) { + return CreateNode(er.Name, er.GetResourceStream()); + } + return null; + } + + public ILSpyTreeNode CreateNode(string key, Stream data) + { + foreach (string fileExt in imageFileExtensions) { + if (key.EndsWith(fileExt, StringComparison.OrdinalIgnoreCase)) + return new CursorResourceEntryNode(key, data); + } + return null; + } + } + + sealed class CursorResourceEntryNode : ResourceEntryNode + { + public CursorResourceEntryNode(string key, Stream data) + : base(key, data) + { + } + + public override object Icon + { + get { return Images.ResourceImage; } + } + + public override bool View(DecompilerTextView textView) + { + try { + AvalonEditTextOutput output = new AvalonEditTextOutput(); + Data.Position = 0; + BitmapImage image = new BitmapImage(); + + //HACK: windows imaging does not understand that .cur files have the same layout as .ico + // so load to data, and modify the ResourceType in the header to make look like an icon... + byte[] curData = ((MemoryStream)Data).ToArray(); + curData[2] = 1; + using (Stream stream = new MemoryStream(curData)) { + image.BeginInit(); + image.StreamSource = stream; + image.EndInit(); + } + + output.AddUIElement(() => new Image { Source = image }); + output.WriteLine(); + output.AddButton(Images.Save, "Save", delegate { + Save(null); + }); + textView.ShowNode(output, this, null); + return true; + } + catch (Exception) { + return false; + } + } + } +} diff --git a/ILSpy/TreeNodes/ResourceNodes/IResourceNodeFactory.cs b/ILSpy/TreeNodes/ResourceNodes/IResourceNodeFactory.cs new file mode 100644 index 000000000..49d5f4893 --- /dev/null +++ b/ILSpy/TreeNodes/ResourceNodes/IResourceNodeFactory.cs @@ -0,0 +1,33 @@ +// Copyright (c) 2011 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 Mono.Cecil; + +namespace ICSharpCode.ILSpy.TreeNodes +{ + /// + /// This interface allows plugins to create custom nodes for resources. + /// + public interface IResourceNodeFactory + { + ILSpyTreeNode CreateNode(Resource resource); + ILSpyTreeNode CreateNode(string key, Stream data); + } +} diff --git a/ILSpy/TreeNodes/ResourceNodes/ImageResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceNodes/ImageResourceEntryNode.cs new file mode 100644 index 000000000..94e894961 --- /dev/null +++ b/ILSpy/TreeNodes/ResourceNodes/ImageResourceEntryNode.cs @@ -0,0 +1,87 @@ +// Copyright (c) 2011 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.ComponentModel.Composition; +using System.IO; +using System.Windows.Controls; +using System.Windows.Media.Imaging; +using ICSharpCode.ILSpy.TextView; +using Mono.Cecil; + +namespace ICSharpCode.ILSpy.TreeNodes +{ + [Export(typeof(IResourceNodeFactory))] + sealed class ImageResourceNodeFactory : IResourceNodeFactory + { + static readonly string[] imageFileExtensions = { ".png", ".gif", ".bmp", ".jpg", ".ico" }; + + public ILSpyTreeNode CreateNode(Mono.Cecil.Resource resource) + { + EmbeddedResource er = resource as EmbeddedResource; + if (er != null) { + return CreateNode(er.Name, er.GetResourceStream()); + } + return null; + } + + public ILSpyTreeNode CreateNode(string key, Stream data) + { + foreach (string fileExt in imageFileExtensions) { + if (key.EndsWith(fileExt, StringComparison.OrdinalIgnoreCase)) + return new ImageResourceEntryNode(key, data); + } + return null; + } + } + + sealed class ImageResourceEntryNode : ResourceEntryNode + { + public ImageResourceEntryNode(string key, Stream data) + : base(key, data) + { + } + + public override object Icon + { + get { return Images.ResourceImage; } + } + + public override bool View(DecompilerTextView textView) + { + try { + AvalonEditTextOutput output = new AvalonEditTextOutput(); + Data.Position = 0; + BitmapImage image = new BitmapImage(); + image.BeginInit(); + image.StreamSource = Data; + image.EndInit(); + output.AddUIElement(() => new Image { Source = image }); + output.WriteLine(); + output.AddButton(Images.Save, "Save", delegate { + Save(null); + }); + textView.ShowNode(output, this, null); + return true; + } + catch (Exception) { + return false; + } + } + } +} diff --git a/ILSpy/TreeNodes/ResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs similarity index 64% rename from ILSpy/TreeNodes/ResourceEntryNode.cs rename to ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs index cddab8326..fa2cff797 100644 --- a/ILSpy/TreeNodes/ResourceEntryNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs @@ -17,14 +17,10 @@ // DEALINGS IN THE SOFTWARE. using System; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Threading.Tasks; using System.ComponentModel.Composition; +using System.IO; using System.Windows.Controls; using System.Windows.Media.Imaging; -using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.Decompiler; using ICSharpCode.ILSpy.TextView; using Microsoft.Win32; @@ -95,61 +91,4 @@ namespace ICSharpCode.ILSpy.TreeNodes return true; } } - - [Export(typeof(IResourceNodeFactory))] - sealed class ImageResourceNodeFactory : IResourceNodeFactory - { - static readonly string[] imageFileExtensions = { ".png", ".gif", ".bmp", ".jpg", ".ico" }; - - public ILSpyTreeNode CreateNode(Mono.Cecil.Resource resource) - { - EmbeddedResource er = resource as EmbeddedResource; - if (er != null) { - return CreateNode(er.Name, er.GetResourceStream()); - } - return null; - } - - public ILSpyTreeNode CreateNode(string key, Stream data) - { - foreach (string fileExt in imageFileExtensions) { - if (key.EndsWith(fileExt, StringComparison.OrdinalIgnoreCase)) - return new ImageResourceEntryNode(key, data); - } - return null; - } - } - - sealed class ImageResourceEntryNode : ResourceEntryNode - { - public ImageResourceEntryNode(string key, Stream data) - : base(key, data) - { - } - - public override object Icon - { - get { return Images.ResourceImage; } - } - - public override bool View(DecompilerTextView textView) - { - try { - AvalonEditTextOutput output = new AvalonEditTextOutput(); - Data.Position = 0; - BitmapImage image = new BitmapImage(); - image.BeginInit(); - image.StreamSource = Data; - image.EndInit(); - output.AddUIElement(() => new Image { Source = image }); - output.WriteLine(); - output.AddButton(Images.Save, "Save", delegate { Save(null); }); - textView.ShowNode(output, this, null); - return true; - } - catch (Exception) { - return false; - } - } - } } diff --git a/ILSpy/TreeNodes/ResourceTreeNode.cs b/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs similarity index 74% rename from ILSpy/TreeNodes/ResourceTreeNode.cs rename to ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs index 1c1a1f08a..5fa470d5a 100644 --- a/ILSpy/TreeNodes/ResourceTreeNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs @@ -34,6 +34,10 @@ using Mono.Cecil; namespace ICSharpCode.ILSpy.TreeNodes { + /// + /// This is the default resource entry tree node, which is used if no specific + /// exists for the given resource type. + /// public class ResourceTreeNode : ILSpyTreeNode { Resource r; @@ -132,63 +136,4 @@ namespace ICSharpCode.ILSpy.TreeNodes return result ?? new ResourceTreeNode(resource); } } - - /// - /// This interface allows plugins to create custom nodes for resources. - /// - public interface IResourceNodeFactory - { - ILSpyTreeNode CreateNode(Resource resource); - ILSpyTreeNode CreateNode(string key, Stream data); - } - - [Export(typeof(IResourceNodeFactory))] - sealed class ResourcesFileTreeNodeFactory : IResourceNodeFactory - { - public ILSpyTreeNode CreateNode(Resource resource) - { - EmbeddedResource er = resource as EmbeddedResource; - if (er != null && er.Name.EndsWith(".resources", StringComparison.OrdinalIgnoreCase)) { - return new ResourcesFileTreeNode(er); - } - return null; - } - - public ILSpyTreeNode CreateNode(string key, Stream data) - { - return null; - } - } - - sealed class ResourcesFileTreeNode : ResourceTreeNode - { - public ResourcesFileTreeNode(EmbeddedResource er) : base(er) - { - this.LazyLoading = true; - } - - public override object Icon - { - get { return Images.ResourceResourcesFile; } - } - - protected override void LoadChildren() - { - EmbeddedResource er = this.Resource as EmbeddedResource; - if (er != null) { - Stream s = er.GetResourceStream(); - s.Position = 0; - ResourceReader reader; - try { - reader = new ResourceReader(s); - } catch (ArgumentException) { - return; - } - foreach (DictionaryEntry entry in reader.Cast().OrderBy(e => e.Key.ToString())) { - if (entry.Value is Stream) - Children.Add(ResourceEntryNode.Create(entry.Key.ToString(), (Stream)entry.Value)); - } - } - } - } } diff --git a/ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs b/ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs new file mode 100644 index 000000000..a99e2a989 --- /dev/null +++ b/ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs @@ -0,0 +1,82 @@ +// Copyright (c) 2011 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.Collections; +using System.ComponentModel.Composition; +using System.IO; +using System.Linq; +using System.Resources; +using Mono.Cecil; + +namespace ICSharpCode.ILSpy.TreeNodes +{ + [Export(typeof(IResourceNodeFactory))] + sealed class ResourcesFileTreeNodeFactory : IResourceNodeFactory + { + public ILSpyTreeNode CreateNode(Resource resource) + { + EmbeddedResource er = resource as EmbeddedResource; + if (er != null && er.Name.EndsWith(".resources", StringComparison.OrdinalIgnoreCase)) { + return new ResourcesFileTreeNode(er); + } + return null; + } + + public ILSpyTreeNode CreateNode(string key, Stream data) + { + return null; + } + } + + sealed class ResourcesFileTreeNode : ResourceTreeNode + { + public ResourcesFileTreeNode(EmbeddedResource er) + : base(er) + { + this.LazyLoading = true; + } + + public override object Icon + { + get { return Images.ResourceResourcesFile; } + } + + protected override void LoadChildren() + { + EmbeddedResource er = this.Resource as EmbeddedResource; + if (er != null) { + Stream s = er.GetResourceStream(); + s.Position = 0; + ResourceReader reader; + try { + reader = new ResourceReader(s); + } + catch (ArgumentException) { + return; + } + foreach (DictionaryEntry entry in reader.Cast().OrderBy(e => e.Key.ToString())) { + if (entry.Value is Stream) + Children.Add(ResourceEntryNode.Create(entry.Key.ToString(), (Stream)entry.Value)); + else if (entry.Value is byte[]) + Children.Add(ResourceEntryNode.Create(entry.Key.ToString(), new MemoryStream((byte[])entry.Value))); + } + } + } + } +} diff --git a/ILSpy/TreeNodes/XamlResourceNode.cs b/ILSpy/TreeNodes/ResourceNodes/XamlResourceNode.cs similarity index 100% rename from ILSpy/TreeNodes/XamlResourceNode.cs rename to ILSpy/TreeNodes/ResourceNodes/XamlResourceNode.cs diff --git a/ILSpy/TreeNodes/ResourceNodes/XmlResourceNode.cs b/ILSpy/TreeNodes/ResourceNodes/XmlResourceNode.cs new file mode 100644 index 000000000..ce2f1e1d4 --- /dev/null +++ b/ILSpy/TreeNodes/ResourceNodes/XmlResourceNode.cs @@ -0,0 +1,106 @@ +// Copyright (c) 2011 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.ComponentModel.Composition; +using System.IO; +using System.Threading.Tasks; + +using ICSharpCode.AvalonEdit.Highlighting; +using ICSharpCode.ILSpy.TextView; +using ICSharpCode.ILSpy.TreeNodes; +using Mono.Cecil; + +namespace ICSharpCode.ILSpy.Xaml +{ + [Export(typeof(IResourceNodeFactory))] + sealed class XmlResourceNodeFactory : IResourceNodeFactory + { + private readonly static string[] xmlFileExtensions = { ".xml", ".xsd", ".xslt" }; + + public ILSpyTreeNode CreateNode(Mono.Cecil.Resource resource) + { + EmbeddedResource er = resource as EmbeddedResource; + if (er != null) + return CreateNode(er.Name, er.GetResourceStream()); + return null; + } + + public ILSpyTreeNode CreateNode(string key, Stream data) + { + foreach (string fileExt in xmlFileExtensions) + if (key.EndsWith(fileExt, StringComparison.OrdinalIgnoreCase)) + return new XmlResourceEntryNode(key, data); + return null; + } + } + + sealed class XmlResourceEntryNode : ResourceEntryNode + { + string xml; + + public XmlResourceEntryNode(string key, Stream data) + : base(key, data) + { + } + + public override object Icon + { + get + { + string text = (string)Text; + if (text.EndsWith(".xml", StringComparison.OrdinalIgnoreCase)) + return Images.ResourceXml; + else if (text.EndsWith(".xsd", StringComparison.OrdinalIgnoreCase)) + return Images.ResourceXsd; + else if (text.EndsWith(".xslt", StringComparison.OrdinalIgnoreCase)) + return Images.ResourceXslt; + else + return Images.Resource; + } + } + + public override bool View(DecompilerTextView textView) + { + AvalonEditTextOutput output = new AvalonEditTextOutput(); + IHighlightingDefinition highlighting = null; + + textView.RunWithCancellation( + token => Task.Factory.StartNew( + () => { + try { + // cache read XAML because stream will be closed after first read + if (xml == null) { + using (var reader = new StreamReader(Data)) { + xml = reader.ReadToEnd(); + } + } + output.Write(xml); + highlighting = HighlightingManager.Instance.GetDefinitionByExtension(".xml"); + } + catch (Exception ex) { + output.Write(ex.ToString()); + } + return output; + }), + t => textView.ShowNode(t.Result, this, highlighting) + ); + return true; + } + } +} diff --git a/ILSpy/XmlDoc/XmlDocKeyProvider.cs b/ILSpy/XmlDoc/XmlDocKeyProvider.cs index fc503c115..d4f1e646c 100644 --- a/ILSpy/XmlDoc/XmlDocKeyProvider.cs +++ b/ILSpy/XmlDoc/XmlDocKeyProvider.cs @@ -36,7 +36,7 @@ namespace ICSharpCode.ILSpy.XmlDoc StringBuilder b = new StringBuilder(); if (member is TypeReference) { b.Append("T:"); - AppendTypeName(b, (TypeDefinition)member); + AppendTypeName(b, (TypeReference)member); } else { if (member is FieldReference) b.Append("F:"); diff --git a/Mono.Cecil/symbols/pdb/Mono.Cecil.Pdb.csproj b/Mono.Cecil/symbols/pdb/Mono.Cecil.Pdb.csproj index d0f6fe4ef..2d2dfd273 100644 --- a/Mono.Cecil/symbols/pdb/Mono.Cecil.Pdb.csproj +++ b/Mono.Cecil/symbols/pdb/Mono.Cecil.Pdb.csproj @@ -1,4 +1,4 @@ - + net_4_0_Debug @@ -13,6 +13,7 @@ 512 true ..\..\mono.snk + 0649 true diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/CompilationUnit.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/CompilationUnit.cs index 9a5047719..6053d6848 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/CompilationUnit.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/CompilationUnit.cs @@ -25,6 +25,9 @@ // THE SOFTWARE. using System; using System.Collections.Generic; +using System.Linq; + +using ICSharpCode.NRefactory.Utils; namespace ICSharpCode.NRefactory.CSharp {