diff --git a/.editorconfig b/.editorconfig index b7d98cfea2..64d038bd68 100644 --- a/.editorconfig +++ b/.editorconfig @@ -12,7 +12,7 @@ indent_size = 2 [*.{yml,yaml}] indent_style = space indent_size = 2 -[*.csproj] +[*.{csproj,props}] indent_style = space indent_size = 2 [*.config] @@ -119,3 +119,53 @@ csharp_space_between_method_declaration_name_and_open_parenthesis = false csharp_space_between_method_declaration_parameter_list_parentheses = false csharp_space_between_parentheses = false csharp_space_between_square_brackets = false + +# Naming rules +dotnet_naming_rule.constants_rule.severity = warning +dotnet_naming_rule.constants_rule.style = upper_camel_case_style +dotnet_naming_rule.constants_rule.symbols = constants_symbols +dotnet_naming_rule.private_constants_rule.severity = warning +dotnet_naming_rule.private_constants_rule.style = upper_camel_case_style +dotnet_naming_rule.private_constants_rule.symbols = private_constants_symbols +dotnet_naming_rule.private_instance_fields_rule.severity = warning +dotnet_naming_rule.private_instance_fields_rule.style = lower_camel_case_style +dotnet_naming_rule.private_instance_fields_rule.symbols = private_instance_fields_symbols +dotnet_naming_rule.private_static_fields_rule.severity = warning +dotnet_naming_rule.private_static_fields_rule.style = lower_camel_case_style +dotnet_naming_rule.private_static_fields_rule.symbols = private_static_fields_symbols +dotnet_naming_rule.private_static_readonly_rule.severity = warning +dotnet_naming_rule.private_static_readonly_rule.style = upper_camel_case_style +dotnet_naming_rule.private_static_readonly_rule.symbols = private_static_readonly_symbols +dotnet_naming_rule.public_static_fields_rule.severity = warning +dotnet_naming_rule.public_static_fields_rule.style = lower_camel_case_style +dotnet_naming_rule.public_static_fields_rule.symbols = public_static_fields_symbols +dotnet_naming_rule.static_readonly_rule.severity = warning +dotnet_naming_rule.static_readonly_rule.style = upper_camel_case_style +dotnet_naming_rule.static_readonly_rule.symbols = static_readonly_symbols +dotnet_naming_style.lower_camel_case_style.capitalization = camel_case +dotnet_naming_style.upper_camel_case_style.capitalization = pascal_case +dotnet_naming_symbols.constants_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected +dotnet_naming_symbols.constants_symbols.applicable_kinds = field +dotnet_naming_symbols.constants_symbols.required_modifiers = const +dotnet_naming_symbols.private_constants_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_constants_symbols.applicable_kinds = field +dotnet_naming_symbols.private_constants_symbols.required_modifiers = const +dotnet_naming_symbols.private_instance_fields_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_instance_fields_symbols.applicable_kinds = field +dotnet_naming_symbols.private_static_fields_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_static_fields_symbols.applicable_kinds = field +dotnet_naming_symbols.private_static_fields_symbols.required_modifiers = static +dotnet_naming_symbols.private_static_readonly_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_static_readonly_symbols.applicable_kinds = field +dotnet_naming_symbols.private_static_readonly_symbols.required_modifiers = readonly,static +dotnet_naming_symbols.public_static_fields_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected +dotnet_naming_symbols.public_static_fields_symbols.applicable_kinds = field +dotnet_naming_symbols.public_static_fields_symbols.required_modifiers = static +dotnet_naming_symbols.static_readonly_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected +dotnet_naming_symbols.static_readonly_symbols.applicable_kinds = field +dotnet_naming_symbols.static_readonly_symbols.required_modifiers = readonly,static + +# Errors and warnings + +# MEF006: No importing constructor +dotnet_diagnostic.MEF006.severity = silent diff --git a/BuildTools/format.bat b/BuildTools/format.bat index be49dc68ce..74018f350f 100644 --- a/BuildTools/format.bat +++ b/BuildTools/format.bat @@ -1,3 +1,5 @@ @rem This file can be used to trigger the commit hook's formatting, @rem modifying the local formatting even if not committing all changes. -"%ProgramFiles%\Git\usr\bin\bash.exe" BuildTools\pre-commit --format \ No newline at end of file +pushd %~dp0\.. +"%ProgramFiles%\Git\usr\bin\bash.exe" BuildTools\pre-commit --format +popd \ No newline at end of file diff --git a/Directory.Packages.props b/Directory.Packages.props index 41b329b4af..0a153070f0 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -45,7 +45,12 @@ - + + + + + + \ No newline at end of file diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.ruleset b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.ruleset index 66b1288eb4..053189fe10 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.ruleset +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.ruleset @@ -79,4 +79,7 @@ + + + \ No newline at end of file diff --git a/ICSharpCode.ILSpyX/Analyzers/Builtin/AttributeAppliedToAnalyzer.cs b/ICSharpCode.ILSpyX/Analyzers/Builtin/AttributeAppliedToAnalyzer.cs index fa925c9b43..f19b23e413 100644 --- a/ICSharpCode.ILSpyX/Analyzers/Builtin/AttributeAppliedToAnalyzer.cs +++ b/ICSharpCode.ILSpyX/Analyzers/Builtin/AttributeAppliedToAnalyzer.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System.Collections.Generic; +using System.ComponentModel.Composition; using System.Linq; using System.Reflection.Metadata; using System.Threading; @@ -29,6 +30,7 @@ namespace ICSharpCode.ILSpyX.Analyzers.Builtin { [ExportAnalyzer(Header = "Applied To", Order = 10)] + [PartCreationPolicy(CreationPolicy.Shared)] class AttributeAppliedToAnalyzer : IAnalyzer { public IEnumerable Analyze(ISymbol analyzedSymbol, AnalyzerContext context) diff --git a/ICSharpCode.ILSpyX/Analyzers/Builtin/EventImplementedByAnalyzer.cs b/ICSharpCode.ILSpyX/Analyzers/Builtin/EventImplementedByAnalyzer.cs index e928f531a5..e01513d2a4 100644 --- a/ICSharpCode.ILSpyX/Analyzers/Builtin/EventImplementedByAnalyzer.cs +++ b/ICSharpCode.ILSpyX/Analyzers/Builtin/EventImplementedByAnalyzer.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System.Collections.Generic; +using System.ComponentModel.Composition; using System.Diagnostics; using System.Linq; @@ -28,6 +29,7 @@ namespace ICSharpCode.ILSpyX.Analyzers.Builtin /// Shows events that implement an interface event. /// [ExportAnalyzer(Header = "Implemented By", Order = 10)] + [PartCreationPolicy(CreationPolicy.Shared)] class EventImplementedByAnalyzer : IAnalyzer { public IEnumerable Analyze(ISymbol analyzedSymbol, AnalyzerContext context) diff --git a/ICSharpCode.ILSpyX/Analyzers/Builtin/EventOverriddenByAnalyzer.cs b/ICSharpCode.ILSpyX/Analyzers/Builtin/EventOverriddenByAnalyzer.cs index 5db8f0841b..55206ce811 100644 --- a/ICSharpCode.ILSpyX/Analyzers/Builtin/EventOverriddenByAnalyzer.cs +++ b/ICSharpCode.ILSpyX/Analyzers/Builtin/EventOverriddenByAnalyzer.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System.Collections.Generic; +using System.ComponentModel.Composition; using System.Diagnostics; using System.Linq; @@ -28,6 +29,7 @@ namespace ICSharpCode.ILSpyX.Analyzers.Builtin /// Shows events that override an event. /// [ExportAnalyzer(Header = "Overridden By", Order = 20)] + [PartCreationPolicy(CreationPolicy.Shared)] class EventOverriddenByAnalyzer : IAnalyzer { public IEnumerable Analyze(ISymbol analyzedSymbol, AnalyzerContext context) diff --git a/ICSharpCode.ILSpyX/Analyzers/Builtin/FieldAccessAnalyzer.cs b/ICSharpCode.ILSpyX/Analyzers/Builtin/FieldAccessAnalyzer.cs index ba829b0fc2..ad02b999f2 100644 --- a/ICSharpCode.ILSpyX/Analyzers/Builtin/FieldAccessAnalyzer.cs +++ b/ICSharpCode.ILSpyX/Analyzers/Builtin/FieldAccessAnalyzer.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel.Composition; using System.Diagnostics; using System.Linq; using System.Reflection.Metadata; @@ -35,6 +36,7 @@ namespace ICSharpCode.ILSpyX.Analyzers.Builtin /// Finds methods where this field is read. /// [ExportAnalyzer(Header = "Assigned By", Order = 20)] + [PartCreationPolicy(CreationPolicy.Shared)] class AssignedByFieldAccessAnalyzer : FieldAccessAnalyzer { public AssignedByFieldAccessAnalyzer() : base(true) { } @@ -44,6 +46,7 @@ public AssignedByFieldAccessAnalyzer() : base(true) { } /// Finds methods where this field is written. /// [ExportAnalyzer(Header = "Read By", Order = 10)] + [PartCreationPolicy(CreationPolicy.Shared)] class ReadByFieldAccessAnalyzer : FieldAccessAnalyzer { public ReadByFieldAccessAnalyzer() : base(false) { } diff --git a/ICSharpCode.ILSpyX/Analyzers/Builtin/MemberImplementsInterfaceAnalyzer.cs b/ICSharpCode.ILSpyX/Analyzers/Builtin/MemberImplementsInterfaceAnalyzer.cs index a25d48d86c..4090650d38 100644 --- a/ICSharpCode.ILSpyX/Analyzers/Builtin/MemberImplementsInterfaceAnalyzer.cs +++ b/ICSharpCode.ILSpyX/Analyzers/Builtin/MemberImplementsInterfaceAnalyzer.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System.Collections.Generic; +using System.ComponentModel.Composition; using System.Diagnostics; using System.Linq; @@ -28,6 +29,7 @@ namespace ICSharpCode.ILSpyX.Analyzers.Builtin /// Shows members from all corresponding interfaces the selected member implements. /// [ExportAnalyzer(Header = "Implements", Order = 40)] + [PartCreationPolicy(CreationPolicy.Shared)] class MemberImplementsInterfaceAnalyzer : IAnalyzer { public IEnumerable Analyze(ISymbol analyzedSymbol, AnalyzerContext context) diff --git a/ICSharpCode.ILSpyX/Analyzers/Builtin/MethodImplementedByAnalyzer.cs b/ICSharpCode.ILSpyX/Analyzers/Builtin/MethodImplementedByAnalyzer.cs index 89da791ed6..644212fab3 100644 --- a/ICSharpCode.ILSpyX/Analyzers/Builtin/MethodImplementedByAnalyzer.cs +++ b/ICSharpCode.ILSpyX/Analyzers/Builtin/MethodImplementedByAnalyzer.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System.Collections.Generic; +using System.ComponentModel.Composition; using System.Diagnostics; using System.Linq; @@ -28,6 +29,7 @@ namespace ICSharpCode.ILSpyX.Analyzers.Builtin /// Shows methods that implement an interface method. /// [ExportAnalyzer(Header = "Implemented By", Order = 40)] + [PartCreationPolicy(CreationPolicy.Shared)] class MethodImplementedByAnalyzer : IAnalyzer { public IEnumerable Analyze(ISymbol analyzedSymbol, AnalyzerContext context) diff --git a/ICSharpCode.ILSpyX/Analyzers/Builtin/MethodOverriddenByAnalyzer.cs b/ICSharpCode.ILSpyX/Analyzers/Builtin/MethodOverriddenByAnalyzer.cs index ab9c4fe71f..c2156d846f 100644 --- a/ICSharpCode.ILSpyX/Analyzers/Builtin/MethodOverriddenByAnalyzer.cs +++ b/ICSharpCode.ILSpyX/Analyzers/Builtin/MethodOverriddenByAnalyzer.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System.Collections.Generic; +using System.ComponentModel.Composition; using System.Diagnostics; using System.Linq; @@ -28,6 +29,7 @@ namespace ICSharpCode.ILSpyX.Analyzers.Builtin /// Shows methods that override a method. /// [ExportAnalyzer(Header = "Overridden By", Order = 30)] + [PartCreationPolicy(CreationPolicy.Shared)] class MethodOverriddenByAnalyzer : IAnalyzer { const GetMemberOptions Options = GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions; diff --git a/ICSharpCode.ILSpyX/Analyzers/Builtin/MethodUsedByAnalyzer.cs b/ICSharpCode.ILSpyX/Analyzers/Builtin/MethodUsedByAnalyzer.cs index 702d77a7d9..a2cb798280 100644 --- a/ICSharpCode.ILSpyX/Analyzers/Builtin/MethodUsedByAnalyzer.cs +++ b/ICSharpCode.ILSpyX/Analyzers/Builtin/MethodUsedByAnalyzer.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel.Composition; using System.Diagnostics; using System.Linq; using System.Reflection.Metadata; @@ -32,6 +33,7 @@ namespace ICSharpCode.ILSpyX.Analyzers.Builtin /// Shows entities that are used by a method. /// [ExportAnalyzer(Header = "Used By", Order = 20)] + [PartCreationPolicy(CreationPolicy.Shared)] class MethodUsedByAnalyzer : IAnalyzer { const GetMemberOptions Options = GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions; diff --git a/ICSharpCode.ILSpyX/Analyzers/Builtin/MethodUsesAnalyzer.cs b/ICSharpCode.ILSpyX/Analyzers/Builtin/MethodUsesAnalyzer.cs index 2b361ec000..6c7576f7c8 100644 --- a/ICSharpCode.ILSpyX/Analyzers/Builtin/MethodUsesAnalyzer.cs +++ b/ICSharpCode.ILSpyX/Analyzers/Builtin/MethodUsesAnalyzer.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel.Composition; using System.Linq; using System.Reflection.Metadata; @@ -32,6 +33,7 @@ namespace ICSharpCode.ILSpyX.Analyzers.Builtin /// Shows entities that are used by a method. /// [ExportAnalyzer(Header = "Uses", Order = 10)] + [PartCreationPolicy(CreationPolicy.Shared)] class MethodUsesAnalyzer : IAnalyzer { public bool Show(ISymbol symbol) => symbol is IMethod method && method.HasBody; diff --git a/ICSharpCode.ILSpyX/Analyzers/Builtin/MethodVirtualUsedByAnalyzer.cs b/ICSharpCode.ILSpyX/Analyzers/Builtin/MethodVirtualUsedByAnalyzer.cs index a24787a880..58d70f3dae 100644 --- a/ICSharpCode.ILSpyX/Analyzers/Builtin/MethodVirtualUsedByAnalyzer.cs +++ b/ICSharpCode.ILSpyX/Analyzers/Builtin/MethodVirtualUsedByAnalyzer.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System.Collections.Generic; +using System.ComponentModel.Composition; using System.Diagnostics; using System.Linq; using System.Reflection.Metadata; @@ -31,6 +32,7 @@ namespace ICSharpCode.ILSpyX.Analyzers.Builtin /// Shows entities that are used by a method. /// [ExportAnalyzer(Header = "Used By", Order = 20)] + [PartCreationPolicy(CreationPolicy.Shared)] class MethodVirtualUsedByAnalyzer : IAnalyzer { const GetMemberOptions Options = GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions; diff --git a/ICSharpCode.ILSpyX/Analyzers/Builtin/PropertyImplementedByAnalyzer.cs b/ICSharpCode.ILSpyX/Analyzers/Builtin/PropertyImplementedByAnalyzer.cs index 4188822f40..159d0cf5f4 100644 --- a/ICSharpCode.ILSpyX/Analyzers/Builtin/PropertyImplementedByAnalyzer.cs +++ b/ICSharpCode.ILSpyX/Analyzers/Builtin/PropertyImplementedByAnalyzer.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System.Collections.Generic; +using System.ComponentModel.Composition; using System.Diagnostics; using System.Linq; @@ -28,6 +29,7 @@ namespace ICSharpCode.ILSpyX.Analyzers.Builtin /// Shows properties that implement an interface property. /// [ExportAnalyzer(Header = "Implemented By", Order = 10)] + [PartCreationPolicy(CreationPolicy.Shared)] class PropertyImplementedByAnalyzer : IAnalyzer { public IEnumerable Analyze(ISymbol analyzedSymbol, AnalyzerContext context) diff --git a/ICSharpCode.ILSpyX/Analyzers/Builtin/PropertyOverriddenByAnalyzer.cs b/ICSharpCode.ILSpyX/Analyzers/Builtin/PropertyOverriddenByAnalyzer.cs index 1eb0d4a004..da0f9f8acd 100644 --- a/ICSharpCode.ILSpyX/Analyzers/Builtin/PropertyOverriddenByAnalyzer.cs +++ b/ICSharpCode.ILSpyX/Analyzers/Builtin/PropertyOverriddenByAnalyzer.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System.Collections.Generic; +using System.ComponentModel.Composition; using System.Diagnostics; using System.Linq; @@ -28,6 +29,7 @@ namespace ICSharpCode.ILSpyX.Analyzers.Builtin /// Shows properties that override a property. /// [ExportAnalyzer(Header = "Overridden By", Order = 20)] + [PartCreationPolicy(CreationPolicy.Shared)] class PropertyOverriddenByAnalyzer : IAnalyzer { public IEnumerable Analyze(ISymbol analyzedSymbol, AnalyzerContext context) diff --git a/ICSharpCode.ILSpyX/Analyzers/Builtin/TypeExposedByAnalyzer.cs b/ICSharpCode.ILSpyX/Analyzers/Builtin/TypeExposedByAnalyzer.cs index 3f4fd59b21..3a2acc8a19 100644 --- a/ICSharpCode.ILSpyX/Analyzers/Builtin/TypeExposedByAnalyzer.cs +++ b/ICSharpCode.ILSpyX/Analyzers/Builtin/TypeExposedByAnalyzer.cs @@ -17,17 +17,18 @@ // DEALINGS IN THE SOFTWARE. using System.Collections.Generic; +using System.ComponentModel.Composition; using System.Diagnostics; +using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.ILSpyX.Analyzers.Builtin { - using ICSharpCode.Decompiler.TypeSystem; - /// /// Finds all entities that expose a type. /// [ExportAnalyzer(Header = "Exposed By", Order = 40)] + [PartCreationPolicy(CreationPolicy.Shared)] class TypeExposedByAnalyzer : IAnalyzer { public bool Show(ISymbol entity) => entity is ITypeDefinition; @@ -143,4 +144,4 @@ bool TypeIsExposedBy(TypeDefinitionUsedVisitor visitor, IMethod method) return visitor.Found; } } -} +} \ No newline at end of file diff --git a/ICSharpCode.ILSpyX/Analyzers/Builtin/TypeExtensionMethodsAnalyzer.cs b/ICSharpCode.ILSpyX/Analyzers/Builtin/TypeExtensionMethodsAnalyzer.cs index b557110212..2e28cd526a 100644 --- a/ICSharpCode.ILSpyX/Analyzers/Builtin/TypeExtensionMethodsAnalyzer.cs +++ b/ICSharpCode.ILSpyX/Analyzers/Builtin/TypeExtensionMethodsAnalyzer.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System.Collections.Generic; +using System.ComponentModel.Composition; using System.Diagnostics; using ICSharpCode.Decompiler.TypeSystem; @@ -27,6 +28,7 @@ namespace ICSharpCode.ILSpyX.Analyzers.Builtin /// Finds all extension methods defined for a type. /// [ExportAnalyzer(Header = "Extension Methods", Order = 50)] + [PartCreationPolicy(CreationPolicy.Shared)] class TypeExtensionMethodsAnalyzer : IAnalyzer { public bool Show(ISymbol symbol) => symbol is ITypeDefinition entity && !entity.IsStatic; diff --git a/ICSharpCode.ILSpyX/Analyzers/Builtin/TypeInstantiatedByAnalyzer.cs b/ICSharpCode.ILSpyX/Analyzers/Builtin/TypeInstantiatedByAnalyzer.cs index 603f3e5b0f..03bf391557 100644 --- a/ICSharpCode.ILSpyX/Analyzers/Builtin/TypeInstantiatedByAnalyzer.cs +++ b/ICSharpCode.ILSpyX/Analyzers/Builtin/TypeInstantiatedByAnalyzer.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel.Composition; using System.Diagnostics; using System.Linq; using System.Reflection.Metadata; @@ -33,6 +34,7 @@ namespace ICSharpCode.ILSpyX.Analyzers.Builtin /// Shows methods that instantiate a type. /// [ExportAnalyzer(Header = "Instantiated By", Order = 20)] + [PartCreationPolicy(CreationPolicy.Shared)] class TypeInstantiatedByAnalyzer : IAnalyzer { const GetMemberOptions Options = GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions; diff --git a/ICSharpCode.ILSpyX/Analyzers/Builtin/TypeUsedByAnalyzer.cs b/ICSharpCode.ILSpyX/Analyzers/Builtin/TypeUsedByAnalyzer.cs index e4a10801f2..b7a8a4147d 100644 --- a/ICSharpCode.ILSpyX/Analyzers/Builtin/TypeUsedByAnalyzer.cs +++ b/ICSharpCode.ILSpyX/Analyzers/Builtin/TypeUsedByAnalyzer.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.ComponentModel.Composition; using System.Diagnostics; using System.Linq; using System.Reflection.Metadata; @@ -34,6 +35,7 @@ namespace ICSharpCode.ILSpyX.Analyzers.Builtin /// Shows entities that use a type. /// [ExportAnalyzer(Header = "Used By", Order = 30)] + [PartCreationPolicy(CreationPolicy.Shared)] class TypeUsedByAnalyzer : IAnalyzer { public IEnumerable Analyze(ISymbol analyzedSymbol, AnalyzerContext context) diff --git a/ICSharpCode.ILSpyX/TreeView/SharpTreeNode.cs b/ICSharpCode.ILSpyX/TreeView/SharpTreeNode.cs index 79686bdbad..e34675fc3c 100644 --- a/ICSharpCode.ILSpyX/TreeView/SharpTreeNode.cs +++ b/ICSharpCode.ILSpyX/TreeView/SharpTreeNode.cs @@ -182,7 +182,7 @@ public bool IsSelected { #endregion #region OnChildrenChanged - internal protected virtual void OnChildrenChanged(NotifyCollectionChangedEventArgs e) + protected internal virtual void OnChildrenChanged(NotifyCollectionChangedEventArgs e) { if (e.OldItems != null) { diff --git a/ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs b/ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs index 32f71afa95..ecb1e25d14 100644 --- a/ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs +++ b/ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs @@ -32,6 +32,7 @@ namespace ILSpy.BamlDecompiler { [Export(typeof(IResourceNodeFactory))] + [PartCreationPolicy(CreationPolicy.Shared)] public sealed class BamlResourceNodeFactory : IResourceNodeFactory { public ITreeNode CreateNode(Resource resource) @@ -44,6 +45,7 @@ public ITreeNode CreateNode(Resource resource) } [Export(typeof(IResourceFileHandler))] + [PartCreationPolicy(CreationPolicy.Shared)] public sealed class BamlResourceFileHandler : IResourceFileHandler { public string EntryType => "Page"; diff --git a/ILSpy.ReadyToRun/ReadyToRunLanguage.cs b/ILSpy.ReadyToRun/ReadyToRunLanguage.cs index cadd1217bc..410e49b67e 100644 --- a/ILSpy.ReadyToRun/ReadyToRunLanguage.cs +++ b/ILSpy.ReadyToRun/ReadyToRunLanguage.cs @@ -95,6 +95,7 @@ public void WriteReference(IMember member, string text, bool isDefinition = fals #endif [Export(typeof(Language))] + [PartCreationPolicy(CreationPolicy.Shared)] internal class ReadyToRunLanguage : Language { private static readonly ConditionalWeakTable readyToRunReaders = new ConditionalWeakTable(); diff --git a/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml.cs b/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml.cs index 53da7a71fe..fbe046ea4a 100644 --- a/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml.cs +++ b/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System.ComponentModel; +using System.ComponentModel.Composition; using System.Windows.Controls; using System.Xml.Linq; @@ -26,6 +27,7 @@ namespace ICSharpCode.ILSpy.ReadyToRun { [ExportOptionPage(Title = nameof(global::ILSpy.ReadyToRun.Properties.Resources.ReadyToRun), Order = 40)] + [PartCreationPolicy(CreationPolicy.NonShared)] partial class ReadyToRunOptionPage : UserControl, IOptionPage { public ReadyToRunOptionPage() diff --git a/ILSpy.sln b/ILSpy.sln index 5d4e13a931..ce506f4d2c 100644 --- a/ILSpy.sln +++ b/ILSpy.sln @@ -36,6 +36,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ICSharpCode.ILSpyX", "ICSha EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ICSharpCode.BamlDecompiler", "ICSharpCode.BamlDecompiler\ICSharpCode.BamlDecompiler.csproj", "{81A30182-3378-4952-8880-F44822390040}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D0858E90-DCD5-4FD9-AA53-7262FAB8BEDB}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + Directory.Packages.props = Directory.Packages.props + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/ILSpy/AboutPage.cs b/ILSpy/AboutPage.cs index b796fa3622..43303bf889 100644 --- a/ILSpy/AboutPage.cs +++ b/ILSpy/AboutPage.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.ComponentModel.Composition; using System.IO; using System.Text.RegularExpressions; using System.Windows; @@ -36,6 +37,7 @@ namespace ICSharpCode.ILSpy { [ExportMainMenuCommand(ParentMenuID = nameof(Resources._Help), Header = nameof(Resources._About), MenuOrder = 99999)] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class AboutPage : SimpleCommand { public override void Execute(object parameter) diff --git a/ILSpy/Analyzers/AnalyzeCommand.cs b/ILSpy/Analyzers/AnalyzeCommand.cs index e221235e83..889e6afa0b 100644 --- a/ILSpy/Analyzers/AnalyzeCommand.cs +++ b/ILSpy/Analyzers/AnalyzeCommand.cs @@ -16,10 +16,9 @@ // 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 ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.TreeNodes; @@ -27,8 +26,11 @@ namespace ICSharpCode.ILSpy.Analyzers { [ExportContextMenuEntry(Header = nameof(Resources.Analyze), Icon = "Images/Search", Category = nameof(Resources.Analyze), InputGestureText = "Ctrl+R", Order = 100)] - internal sealed class AnalyzeCommand : SimpleCommand, IContextMenuEntry + [PartCreationPolicy(CreationPolicy.Shared)] + internal sealed class AnalyzeContextMenuCommand : IContextMenuEntry { + private static readonly AnalyzerTreeViewModel AnalyzerTreeView = App.ExportProvider.GetExportedValue(); + public bool IsVisible(TextViewContext context) { if (context.TreeView is AnalyzerTreeView && context.SelectedTreeNodes != null && context.SelectedTreeNodes.All(n => n.Parent.IsRoot)) @@ -41,70 +43,49 @@ public bool IsVisible(TextViewContext context) public bool IsEnabled(TextViewContext context) { if (context.SelectedTreeNodes == null) - return context.Reference != null && context.Reference.Reference is IEntity; - foreach (IMemberTreeNode node in context.SelectedTreeNodes) { - if (!IsValidReference(node.Member)) - return false; + return context.Reference is { Reference: IEntity }; } - - return true; + return context.SelectedTreeNodes + .OfType() + .All(node => IsValidReference(node.Member)); } - bool IsValidReference(object reference) + static bool IsValidReference(object reference) { - return reference is IEntity && !(reference is IField f && f.IsConst); + return reference is IEntity and not IField { IsConst: true }; } public void Execute(TextViewContext context) { - AnalyzerTreeView analyzerTreeView = MainWindow.Instance.AnalyzerTreeView; - if (analyzerTreeView == null) - { - return; - } if (context.SelectedTreeNodes != null) { - foreach (IMemberTreeNode node in context.SelectedTreeNodes) + foreach (var node in context.SelectedTreeNodes.OfType().ToArray()) { - analyzerTreeView.Analyze(node.Member); + AnalyzerTreeView.Analyze(node.Member); } } - else if (context.Reference != null && context.Reference.Reference is IEntity entity) + else if (context.Reference is { Reference: IEntity entity }) { - analyzerTreeView.Analyze(entity); + AnalyzerTreeView.Analyze(entity); } } + } + + internal sealed class AnalyzeCommand : SimpleCommand + { + private static readonly AnalyzerTreeViewModel AnalyzerTreeView = App.ExportProvider.GetExportedValue(); public override bool CanExecute(object parameter) { - AnalyzerTreeView analyzerTreeView = MainWindow.Instance.AnalyzerTreeView; - if (analyzerTreeView != null && analyzerTreeView.IsKeyboardFocusWithin) - { - return analyzerTreeView.SelectedItems.OfType().All(n => n is IMemberTreeNode); - } - else - { - return MainWindow.Instance.SelectedNodes.All(n => n is IMemberTreeNode); - } + return MainWindow.Instance.SelectedNodes.All(n => n is IMemberTreeNode); } public override void Execute(object parameter) { - AnalyzerTreeView analyzerTreeView = MainWindow.Instance.AnalyzerTreeView; - if (analyzerTreeView != null && analyzerTreeView.IsKeyboardFocusWithin) - { - foreach (IMemberTreeNode node in MainWindow.Instance.AnalyzerTreeView.SelectedItems.OfType().ToArray()) - { - MainWindow.Instance.AnalyzerTreeView.Analyze(node.Member); - } - } - else + foreach (var node in MainWindow.Instance.SelectedNodes.OfType()) { - foreach (IMemberTreeNode node in MainWindow.Instance.SelectedNodes) - { - MainWindow.Instance.AnalyzerTreeView.Analyze(node.Member); - } + AnalyzerTreeView.Analyze(node.Member); } } } diff --git a/ILSpy/Analyzers/AnalyzerRootNode.cs b/ILSpy/Analyzers/AnalyzerRootNode.cs new file mode 100644 index 0000000000..17709687f9 --- /dev/null +++ b/ILSpy/Analyzers/AnalyzerRootNode.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; + +using ICSharpCode.ILSpy.Util; +using ICSharpCode.ILSpyX; +using ICSharpCode.ILSpyX.TreeView; + +namespace ICSharpCode.ILSpy.Analyzers; + +public sealed class AnalyzerRootNode : AnalyzerTreeNode +{ + public AnalyzerRootNode() + { + MessageBus.Subscribers += (sender, e) => CurrentAssemblyList_Changed(sender, e); + } + + void CurrentAssemblyList_Changed(object sender, NotifyCollectionChangedEventArgs e) + { + if (e.Action == NotifyCollectionChangedAction.Reset) + { + this.Children.Clear(); + } + else + { + var removedAssemblies = e.OldItems?.Cast().ToArray() ?? []; + var addedAssemblies = e.NewItems?.Cast().ToArray() ?? []; + + HandleAssemblyListChanged(removedAssemblies, addedAssemblies); + } + } + + public override bool HandleAssemblyListChanged(ICollection removedAssemblies, ICollection addedAssemblies) + { + this.Children.RemoveAll( + delegate (SharpTreeNode n) { + AnalyzerTreeNode an = n as AnalyzerTreeNode; + return an == null || !an.HandleAssemblyListChanged(removedAssemblies, addedAssemblies); + }); + return true; + } +} \ No newline at end of file diff --git a/ILSpy/Analyzers/AnalyzerSearchTreeNode.cs b/ILSpy/Analyzers/AnalyzerSearchTreeNode.cs index 490bf5ec2a..0a7bbb95cb 100644 --- a/ILSpy/Analyzers/AnalyzerSearchTreeNode.cs +++ b/ILSpy/Analyzers/AnalyzerSearchTreeNode.cs @@ -86,29 +86,17 @@ AnalyzerTreeNode SymbolTreeNodeFactory(ISymbol symbol) switch (symbol) { case IModule module: - return new AnalyzedModuleTreeNode(module) { - Language = this.Language - }; + return new AnalyzedModuleTreeNode(module) { }; case ITypeDefinition td: - return new AnalyzedTypeTreeNode(td) { - Language = this.Language - }; + return new AnalyzedTypeTreeNode(td) { }; case IField fd: - return new AnalyzedFieldTreeNode(fd) { - Language = this.Language - }; + return new AnalyzedFieldTreeNode(fd) { }; case IMethod md: - return new AnalyzedMethodTreeNode(md) { - Language = this.Language - }; + return new AnalyzedMethodTreeNode(md) { }; case IProperty pd: - return new AnalyzedPropertyTreeNode(pd) { - Language = this.Language - }; + return new AnalyzedPropertyTreeNode(pd) { }; case IEvent ed: - return new AnalyzedEventTreeNode(ed) { - Language = this.Language - }; + return new AnalyzedEventTreeNode(ed) { }; default: throw new ArgumentOutOfRangeException(nameof(symbol), $"Symbol {symbol.GetType().FullName} is not supported."); } diff --git a/ILSpy/Analyzers/AnalyzerTreeNode.cs b/ILSpy/Analyzers/AnalyzerTreeNode.cs index e2946c3e06..8972926a73 100644 --- a/ILSpy/Analyzers/AnalyzerTreeNode.cs +++ b/ILSpy/Analyzers/AnalyzerTreeNode.cs @@ -17,9 +17,8 @@ // DEALINGS IN THE SOFTWARE. using System.Collections.Generic; -using System.Collections.Specialized; -using System.Linq; +using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX; using ICSharpCode.ILSpyX.TreeView; @@ -27,23 +26,11 @@ namespace ICSharpCode.ILSpy.Analyzers { public abstract class AnalyzerTreeNode : SharpTreeNode { - private Language language; - - public Language Language { - get { return language; } - set { - if (language != value) - { - language = value; - foreach (var child in this.Children.OfType()) - child.Language = value; - } - } - } + public Language Language => SettingsService.Instance.SessionSettings.LanguageSettings.Language; public override bool CanDelete() { - return Parent != null && Parent.IsRoot; + return Parent is { IsRoot: true }; } public override void DeleteCore() @@ -56,16 +43,6 @@ public override void Delete() DeleteCore(); } - internal protected override void OnChildrenChanged(NotifyCollectionChangedEventArgs e) - { - if (e.NewItems != null) - { - foreach (AnalyzerTreeNode a in e.NewItems.OfType()) - a.Language = this.Language; - } - base.OnChildrenChanged(e); - } - /// /// Handles changes to the assembly list. /// diff --git a/ILSpy/Analyzers/AnalyzerTreeView.cs b/ILSpy/Analyzers/AnalyzerTreeView.cs deleted file mode 100644 index 4e40239ea8..0000000000 --- a/ILSpy/Analyzers/AnalyzerTreeView.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.Collections.Generic; -using System.Collections.Specialized; -using System.Linq; -using System.Windows; - -using ICSharpCode.Decompiler.TypeSystem; -using ICSharpCode.ILSpy.Analyzers.TreeNodes; -using ICSharpCode.ILSpy.Docking; -using ICSharpCode.ILSpy.ViewModels; -using ICSharpCode.ILSpyX; -using ICSharpCode.ILSpy.Controls.TreeView; -using ICSharpCode.ILSpyX.TreeView; - -namespace ICSharpCode.ILSpy.Analyzers -{ - /// - /// Analyzer tree view. - /// - public class AnalyzerTreeView : SharpTreeView - { - FilterSettings filterSettings; - - public AnalyzerTreeView() - { - this.ShowRoot = false; - this.Root = new AnalyzerRootNode { Language = MainWindow.Instance.CurrentLanguage }; - this.BorderThickness = new Thickness(0); - ContextMenuProvider.Add(this); - MainWindow.Instance.CurrentAssemblyListChanged += MainWindow_Instance_CurrentAssemblyListChanged; - DockWorkspace.Instance.PropertyChanged += DockWorkspace_PropertyChanged; - filterSettings = MainWindow.Instance.SessionSettings.FilterSettings; - filterSettings.PropertyChanged += FilterSettings_PropertyChanged; - } - - private void DockWorkspace_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) - { - switch (e.PropertyName) - { - case nameof(DockWorkspace.Instance.ActiveTabPage): - filterSettings.PropertyChanged -= FilterSettings_PropertyChanged; - filterSettings = DockWorkspace.Instance.ActiveTabPage.FilterSettings; - filterSettings.PropertyChanged += FilterSettings_PropertyChanged; - break; - } - } - - private void FilterSettings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) - { - switch (e.PropertyName) - { - case "Language": - case "LanguageVersion": - ((AnalyzerRootNode)this.Root).Language = MainWindow.Instance.CurrentLanguage; - break; - } - } - - void MainWindow_Instance_CurrentAssemblyListChanged(object sender, NotifyCollectionChangedEventArgs e) - { - if (e.Action == NotifyCollectionChangedAction.Reset) - { - this.Root.Children.Clear(); - } - else - { - List removedAssemblies = new List(); - if (e.OldItems != null) - removedAssemblies.AddRange(e.OldItems.Cast()); - List addedAssemblies = new List(); - if (e.NewItems != null) - addedAssemblies.AddRange(e.NewItems.Cast()); - ((AnalyzerRootNode)this.Root).HandleAssemblyListChanged(removedAssemblies, addedAssemblies); - } - } - - public void Show() - { - DockWorkspace.Instance.ShowToolPane(AnalyzerPaneModel.PaneContentId); - } - - public void Show(AnalyzerTreeNode node) - { - Show(); - - node.IsExpanded = true; - this.Root.Children.Add(node); - this.SelectedItem = node; - this.FocusNode(node); - } - - public void ShowOrFocus(AnalyzerTreeNode node) - { - if (node is AnalyzerEntityTreeNode) - { - var an = node as AnalyzerEntityTreeNode; - var found = this.Root.Children.OfType().FirstOrDefault(n => n.Member == an.Member); - if (found != null) - { - Show(); - - found.IsExpanded = true; - this.SelectedItem = found; - this.FocusNode(found); - return; - } - } - Show(node); - } - - public void Analyze(IEntity entity) - { - if (entity == null) - { - throw new ArgumentNullException(nameof(entity)); - } - - if (entity.MetadataToken.IsNil) - { - MessageBox.Show(Properties.Resources.CannotAnalyzeMissingRef, "ILSpy"); - return; - } - - switch (entity) - { - case ITypeDefinition td: - ShowOrFocus(new AnalyzedTypeTreeNode(td)); - break; - case IField fd: - if (!fd.IsConst) - ShowOrFocus(new AnalyzedFieldTreeNode(fd)); - break; - case IMethod md: - ShowOrFocus(new AnalyzedMethodTreeNode(md)); - break; - case IProperty pd: - ShowOrFocus(new AnalyzedPropertyTreeNode(pd)); - break; - case IEvent ed: - ShowOrFocus(new AnalyzedEventTreeNode(ed)); - break; - default: - throw new ArgumentOutOfRangeException(nameof(entity), $"Entity {entity.GetType().FullName} is not supported."); - } - } - - sealed class AnalyzerRootNode : AnalyzerTreeNode - { - public override bool HandleAssemblyListChanged(ICollection removedAssemblies, ICollection addedAssemblies) - { - this.Children.RemoveAll( - delegate (SharpTreeNode n) { - AnalyzerTreeNode an = n as AnalyzerTreeNode; - return an == null || !an.HandleAssemblyListChanged(removedAssemblies, addedAssemblies); - }); - return true; - } - } - } -} diff --git a/ILSpy/Analyzers/AnalyzerTreeView.xaml b/ILSpy/Analyzers/AnalyzerTreeView.xaml new file mode 100644 index 0000000000..28bba030c8 --- /dev/null +++ b/ILSpy/Analyzers/AnalyzerTreeView.xaml @@ -0,0 +1,23 @@ + + + + + + + \ No newline at end of file diff --git a/ILSpy/Docking/PaneTemplateSelector.cs b/ILSpy/Analyzers/AnalyzerTreeView.xaml.cs similarity index 60% rename from ILSpy/Docking/PaneTemplateSelector.cs rename to ILSpy/Analyzers/AnalyzerTreeView.xaml.cs index 7890a1ddeb..bff239a460 100644 --- a/ILSpy/Docking/PaneTemplateSelector.cs +++ b/ILSpy/Analyzers/AnalyzerTreeView.xaml.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2019 AlphaSierraPapa for the SharpDevelop Team +// 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 @@ -16,33 +16,35 @@ // 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.ObjectModel; -using System.Linq; -using System.Windows; +using System.ComponentModel.Composition; using System.Windows.Controls; -namespace ICSharpCode.ILSpy.Docking -{ - public class TemplateMapping - { - public Type Type { get; set; } - public DataTemplate Template { get; set; } - } +using ICSharpCode.ILSpyX.TreeView; + +using TomsToolbox.Wpf.Composition.Mef; - public class PaneTemplateSelector : DataTemplateSelector +namespace ICSharpCode.ILSpy.Analyzers +{ + /// + /// Interaction logic for AnalyzerTreeView.xaml + /// + [DataTemplate(typeof(AnalyzerTreeViewModel))] + [PartCreationPolicy(CreationPolicy.NonShared)] + [Export] + public partial class AnalyzerTreeView { - public Collection Mappings { get; set; } = new Collection(); + public AnalyzerTreeView() + { + InitializeComponent(); + ContextMenuProvider.Add(this); + } - public override DataTemplate SelectTemplate(object item, DependencyObject container) + private void AnalyzerTreeView_OnSelectionChanged(object sender, SelectionChangedEventArgs e) { - if (item == null) + if (SelectedItem is SharpTreeNode sharpTreeNode) { - return base.SelectTemplate(item, container); + FocusNode(sharpTreeNode); } - - return Mappings.FirstOrDefault(m => m.Type == item.GetType())?.Template - ?? base.SelectTemplate(item, container); } } } diff --git a/ILSpy/Analyzers/AnalyzerTreeViewModel.cs b/ILSpy/Analyzers/AnalyzerTreeViewModel.cs new file mode 100644 index 0000000000..fc7def7558 --- /dev/null +++ b/ILSpy/Analyzers/AnalyzerTreeViewModel.cs @@ -0,0 +1,130 @@ +// Copyright (c) 2019 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; +using System.Linq; +using System.Windows; +using System.Windows.Input; + +using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.ILSpy.Analyzers.TreeNodes; +using ICSharpCode.ILSpy.TreeNodes; +using ICSharpCode.ILSpy.ViewModels; + +using TomsToolbox.Wpf; + +namespace ICSharpCode.ILSpy.Analyzers +{ + [ExportToolPane] + [PartCreationPolicy(CreationPolicy.Shared)] + [Export] + public class AnalyzerTreeViewModel : ToolPaneModel + { + private AnalyzerTreeNode selectedItem; + + public const string PaneContentId = "analyzerPane"; + + public AnalyzerTreeViewModel() + { + ContentId = PaneContentId; + Title = Properties.Resources.Analyze; + ShortcutKey = new(Key.R, ModifierKeys.Control); + AssociatedCommand = ILSpyCommands.Analyze; + } + + public AnalyzerRootNode Root { get; } = new(); + + public AnalyzerTreeNode SelectedItem { + get => selectedItem; + set => SetProperty(ref selectedItem, value); + } + + public ICommand AnalyzeCommand => new DelegateCommand(AnalyzeSelected); + + public ObservableCollection SelectedItems { get; } = []; + + private void AnalyzeSelected() + { + foreach (var node in SelectedItems.OfType()) + { + Analyze(node.Member); + } + } + + void AddOrSelect(AnalyzerTreeNode node) + { + Show(); + + AnalyzerTreeNode target = default; + + if (node is AnalyzerEntityTreeNode { Member: { } member }) + { + target = this.Root.Children.OfType().FirstOrDefault(item => item.Member == member); + } + + if (target == null) + { + this.Root.Children.Add(node); + target = node; + } + + target.IsExpanded = true; + this.SelectedItem = target; + } + + public void Analyze(IEntity entity) + { + if (entity == null) + { + throw new ArgumentNullException(nameof(entity)); + } + + if (entity.MetadataToken.IsNil) + { + MessageBox.Show(Properties.Resources.CannotAnalyzeMissingRef, "ILSpy"); + return; + } + + switch (entity) + { + case ITypeDefinition td: + AddOrSelect(new AnalyzedTypeTreeNode(td)); + break; + case IField fd: + if (!fd.IsConst) + AddOrSelect(new AnalyzedFieldTreeNode(fd)); + break; + case IMethod md: + AddOrSelect(new AnalyzedMethodTreeNode(md)); + break; + case IProperty pd: + AddOrSelect(new AnalyzedPropertyTreeNode(pd)); + break; + case IEvent ed: + AddOrSelect(new AnalyzedEventTreeNode(ed)); + break; + default: + throw new ArgumentOutOfRangeException(nameof(entity), $@"Entity {entity.GetType().FullName} is not supported."); + } + } + } +} + diff --git a/ILSpy/Analyzers/CopyAnalysisResultsContextMenuEntry.cs b/ILSpy/Analyzers/CopyAnalysisResultsContextMenuEntry.cs index 1e2566b685..5d0af2d66f 100644 --- a/ILSpy/Analyzers/CopyAnalysisResultsContextMenuEntry.cs +++ b/ILSpy/Analyzers/CopyAnalysisResultsContextMenuEntry.cs @@ -16,6 +16,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System.ComponentModel.Composition; using System.Linq; using System.Text; using System.Windows; @@ -23,6 +24,7 @@ namespace ICSharpCode.ILSpy.Analyzers { [ExportContextMenuEntry(Header = "Copy results", Category = "Analyze", Order = 200)] + [PartCreationPolicy(CreationPolicy.Shared)] internal sealed class CopyAnalysisResultsContextMenuEntry : IContextMenuEntry { public bool IsVisible(TextViewContext context) diff --git a/ILSpy/Analyzers/RemoveAnalyzeContextMenuEntry.cs b/ILSpy/Analyzers/RemoveAnalyzeContextMenuEntry.cs index d3ed19a46b..662f466ded 100644 --- a/ILSpy/Analyzers/RemoveAnalyzeContextMenuEntry.cs +++ b/ILSpy/Analyzers/RemoveAnalyzeContextMenuEntry.cs @@ -16,11 +16,13 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System.ComponentModel.Composition; using System.Linq; namespace ICSharpCode.ILSpy.Analyzers { [ExportContextMenuEntry(Header = "Remove", Icon = "images/Delete", Category = "Analyze", Order = 200)] + [PartCreationPolicy(CreationPolicy.Shared)] internal sealed class RemoveAnalyzeContextMenuEntry : IContextMenuEntry { public bool IsVisible(TextViewContext context) diff --git a/ILSpy/App.xaml.cs b/ILSpy/App.xaml.cs index 77722c3ae8..d0b08f0f59 100644 --- a/ILSpy/App.xaml.cs +++ b/ILSpy/App.xaml.cs @@ -42,6 +42,10 @@ using TomsToolbox.Wpf.Styles; using ICSharpCode.ILSpyX.TreeView; +using TomsToolbox.Composition; +using TomsToolbox.Wpf.Composition; +using System.ComponentModel.Composition.Hosting; + namespace ICSharpCode.ILSpy { /// @@ -52,8 +56,7 @@ public partial class App : Application internal static CommandLineArguments CommandLineArguments; internal static readonly IList StartupExceptions = new List(); - public static ExportProvider ExportProvider { get; private set; } - public static IExportProviderFactory ExportProviderFactory { get; private set; } + public static IExportProvider ExportProvider { get; private set; } internal class ExceptionData { @@ -89,7 +92,12 @@ public App() } TaskScheduler.UnobservedTaskException += DotNet40_UnobservedTaskException; InitializeMef().GetAwaiter().GetResult(); - Languages.Initialize(ExportProvider); + + // Register the export provider so that it can be accessed from WPF/XAML components. + ExportProviderLocator.Register(ExportProvider); + // Add data templates registered via MEF. + Resources.MergedDictionaries.Add(DataTemplateManager.CreateDynamicDataTemplates(ExportProvider)); + EventManager.RegisterClassHandler(typeof(Window), Hyperlink.RequestNavigateEvent, new RequestNavigateEventHandler(Window_RequestNavigate)); @@ -170,8 +178,9 @@ private static async Task InitializeMef() // If/When any part needs to import ICompositionService, this will be needed: // catalog.WithCompositionService(); var config = CompositionConfiguration.Create(catalog); - ExportProviderFactory = config.CreateExportProviderFactory(); - ExportProvider = ExportProviderFactory.CreateExportProvider(); + var exportProviderFactory = config.CreateExportProviderFactory(); + ExportProvider = new ExportProviderAdapter(exportProviderFactory.CreateExportProvider()); + // This throws exceptions for composition failures. Alternatively, the configuration's CompositionErrors property // could be used to log the errors directly. Used at the end so that it does not prevent the export provider setup. config.ThrowOnErrors(); diff --git a/ILSpy/Commands/BrowseBackCommand.cs b/ILSpy/Commands/BrowseBackCommand.cs index bb85804950..cf424cbd43 100644 --- a/ILSpy/Commands/BrowseBackCommand.cs +++ b/ILSpy/Commands/BrowseBackCommand.cs @@ -16,6 +16,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System.ComponentModel.Composition; using System.Windows.Input; using ICSharpCode.ILSpy.Properties; @@ -23,6 +24,7 @@ namespace ICSharpCode.ILSpy { [ExportToolbarCommand(ToolTip = nameof(Resources.Back), ToolbarIcon = "Images/Back", ToolbarCategory = nameof(Resources.Navigation), ToolbarOrder = 0)] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class BrowseBackCommand : CommandWrapper { public BrowseBackCommand() diff --git a/ILSpy/Commands/BrowseForwardCommand.cs b/ILSpy/Commands/BrowseForwardCommand.cs index 5935bd6663..3a82764576 100644 --- a/ILSpy/Commands/BrowseForwardCommand.cs +++ b/ILSpy/Commands/BrowseForwardCommand.cs @@ -16,6 +16,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System.ComponentModel.Composition; using System.Windows.Input; using ICSharpCode.ILSpy.Properties; @@ -23,6 +24,7 @@ namespace ICSharpCode.ILSpy { [ExportToolbarCommand(ToolTip = nameof(Resources.Forward), ToolbarIcon = "Images/Forward", ToolbarCategory = nameof(Resources.Navigation), ToolbarOrder = 1)] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class BrowseForwardCommand : CommandWrapper { public BrowseForwardCommand() diff --git a/ILSpy/Commands/CheckForUpdatesCommand.cs b/ILSpy/Commands/CheckForUpdatesCommand.cs index 011d682413..58438c6043 100644 --- a/ILSpy/Commands/CheckForUpdatesCommand.cs +++ b/ILSpy/Commands/CheckForUpdatesCommand.cs @@ -17,12 +17,15 @@ // DEALINGS IN THE SOFTWARE. +using System.ComponentModel.Composition; + using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpyX.Settings; namespace ICSharpCode.ILSpy { [ExportMainMenuCommand(ParentMenuID = nameof(Resources._Help), Header = nameof(Resources._CheckUpdates), MenuOrder = 5000)] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class CheckForUpdatesCommand : SimpleCommand { public override bool CanExecute(object parameter) diff --git a/ILSpy/Commands/CopyFullyQualifiedNameContextMenuEntry.cs b/ILSpy/Commands/CopyFullyQualifiedNameContextMenuEntry.cs index 0a197035c1..47490e37bc 100644 --- a/ILSpy/Commands/CopyFullyQualifiedNameContextMenuEntry.cs +++ b/ILSpy/Commands/CopyFullyQualifiedNameContextMenuEntry.cs @@ -15,6 +15,7 @@ // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System.ComponentModel.Composition; using System.Windows; using ICSharpCode.ILSpy.Properties; @@ -23,6 +24,7 @@ namespace ICSharpCode.ILSpy { [ExportContextMenuEntry(Header = nameof(Resources.CopyName), Icon = "images/Copy", Order = 9999)] + [PartCreationPolicy(CreationPolicy.Shared)] public class CopyFullyQualifiedNameContextMenuEntry : IContextMenuEntry { public bool IsVisible(TextViewContext context) diff --git a/ILSpy/Commands/DecompileAllCommand.cs b/ILSpy/Commands/DecompileAllCommand.cs index 7bc61a1678..c905c9a87b 100644 --- a/ILSpy/Commands/DecompileAllCommand.cs +++ b/ILSpy/Commands/DecompileAllCommand.cs @@ -20,6 +20,7 @@ using System; using System.Collections.Concurrent; +using System.ComponentModel.Composition; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; @@ -27,6 +28,7 @@ using ICSharpCode.Decompiler; using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.TextView; +using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX; using TomsToolbox.Essentials; @@ -34,6 +36,7 @@ namespace ICSharpCode.ILSpy { [ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.DEBUGDecompile), MenuCategory = nameof(Resources.Open), MenuOrder = 2.5)] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class DecompileAllCommand : SimpleCommand { public override bool CanExecute(object parameter) @@ -86,12 +89,13 @@ public override void Execute(object parameter) } [ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.DEBUGDecompile100x), MenuCategory = nameof(Resources.Open), MenuOrder = 2.6)] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class Decompile100TimesCommand : SimpleCommand { public override void Execute(object parameter) { const int numRuns = 100; - var language = MainWindow.Instance.CurrentLanguage; + var language = SettingsService.Instance.SessionSettings.LanguageSettings.Language; var nodes = MainWindow.Instance.SelectedNodes.ToArray(); var options = MainWindow.Instance.CreateDecompilationOptions(); Docking.DockWorkspace.Instance.RunWithCancellation(ct => Task.Factory.StartNew(() => { diff --git a/ILSpy/Commands/DecompileCommand.cs b/ILSpy/Commands/DecompileCommand.cs index ff0a995dfe..0a7d1a3857 100644 --- a/ILSpy/Commands/DecompileCommand.cs +++ b/ILSpy/Commands/DecompileCommand.cs @@ -16,6 +16,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System.ComponentModel.Composition; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; @@ -25,6 +26,7 @@ namespace ICSharpCode.ILSpy.Commands { [ExportContextMenuEntry(Header = nameof(Resources.Decompile), Order = 10)] + [PartCreationPolicy(CreationPolicy.Shared)] class DecompileCommand : IContextMenuEntry { public bool IsVisible(TextViewContext context) diff --git a/ILSpy/Commands/DecompileInNewViewCommand.cs b/ILSpy/Commands/DecompileInNewViewCommand.cs index 744c1ecc90..07e9bebcbe 100644 --- a/ILSpy/Commands/DecompileInNewViewCommand.cs +++ b/ILSpy/Commands/DecompileInNewViewCommand.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel.Composition; using System.Linq; using System.Windows.Threading; @@ -30,6 +31,7 @@ namespace ICSharpCode.ILSpy.Commands { [ExportContextMenuEntry(Header = nameof(Resources.DecompileToNewPanel), InputGestureText = "MMB", Icon = "images/Search", Category = nameof(Resources.Analyze), Order = 90)] + [PartCreationPolicy(CreationPolicy.Shared)] internal sealed class DecompileInNewViewCommand : IContextMenuEntry { public bool IsVisible(TextViewContext context) diff --git a/ILSpy/Commands/DisassembleAllCommand.cs b/ILSpy/Commands/DisassembleAllCommand.cs index 34f7f85752..fc44d405a3 100644 --- a/ILSpy/Commands/DisassembleAllCommand.cs +++ b/ILSpy/Commands/DisassembleAllCommand.cs @@ -20,6 +20,7 @@ using System; using System.Collections.Concurrent; +using System.ComponentModel.Composition; using System.Diagnostics; using System.Threading.Tasks; @@ -30,6 +31,7 @@ namespace ICSharpCode.ILSpy { [ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.DEBUGDisassemble), MenuCategory = nameof(Resources.Open), MenuOrder = 2.5)] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class DisassembleAllCommand : SimpleCommand { public override bool CanExecute(object parameter) diff --git a/ILSpy/Commands/ExitCommand.cs b/ILSpy/Commands/ExitCommand.cs index bbb68122c9..fb475ef015 100644 --- a/ILSpy/Commands/ExitCommand.cs +++ b/ILSpy/Commands/ExitCommand.cs @@ -15,11 +15,14 @@ // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System.ComponentModel.Composition; + using ICSharpCode.ILSpy.Properties; namespace ICSharpCode.ILSpy { [ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.E_xit), MenuOrder = 99999, MenuCategory = nameof(Resources.Exit))] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class ExitCommand : SimpleCommand { public override void Execute(object parameter) diff --git a/ILSpy/Commands/ExportCommandAttribute.cs b/ILSpy/Commands/ExportCommandAttribute.cs index 282774bff1..19d100fd48 100644 --- a/ILSpy/Commands/ExportCommandAttribute.cs +++ b/ILSpy/Commands/ExportCommandAttribute.cs @@ -99,21 +99,15 @@ public ExportMainMenuCommandAttribute() #endregion #region Tool Panes - public interface IToolPaneMetadata - { - string ContentId { get; } - } [MetadataAttribute] - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public class ExportToolPaneAttribute : ExportAttribute, IToolPaneMetadata + [AttributeUsage(AttributeTargets.Class)] + public class ExportToolPaneAttribute : ExportAttribute { public ExportToolPaneAttribute() : base("ToolPane", typeof(ViewModels.ToolPaneModel)) { } - - public string ContentId { get; set; } } #endregion } diff --git a/ILSpy/Commands/ExtractPackageEntryContextMenuEntry.cs b/ILSpy/Commands/ExtractPackageEntryContextMenuEntry.cs index 57076e517e..3f22fa1cdc 100644 --- a/ILSpy/Commands/ExtractPackageEntryContextMenuEntry.cs +++ b/ILSpy/Commands/ExtractPackageEntryContextMenuEntry.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.ComponentModel.Composition; using System.Diagnostics; using System.IO; using System.Linq; @@ -34,6 +35,7 @@ namespace ICSharpCode.ILSpy { [ExportContextMenuEntry(Header = nameof(Resources.ExtractPackageEntry), Category = nameof(Resources.Save), Icon = "Images/Save")] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class ExtractPackageEntryContextMenuEntry : IContextMenuEntry { public void Execute(TextViewContext context) diff --git a/ILSpy/Commands/GeneratePdbContextMenuEntry.cs b/ILSpy/Commands/GeneratePdbContextMenuEntry.cs index 773c7cd4c9..b7c3997d06 100644 --- a/ILSpy/Commands/GeneratePdbContextMenuEntry.cs +++ b/ILSpy/Commands/GeneratePdbContextMenuEntry.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.ComponentModel.Composition; using System.Diagnostics; using System.IO; using System.Linq; @@ -38,6 +39,7 @@ namespace ICSharpCode.ILSpy { [ExportContextMenuEntry(Header = nameof(Resources.GeneratePortable))] + [PartCreationPolicy(CreationPolicy.Shared)] class GeneratePdbContextMenuEntry : IContextMenuEntry { public void Execute(TextViewContext context) @@ -103,6 +105,7 @@ internal static void GeneratePdbForAssembly(LoadedAssembly assembly) } [ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.GeneratePortable), MenuCategory = nameof(Resources.Save))] + [PartCreationPolicy(CreationPolicy.Shared)] class GeneratePdbMainMenuEntry : SimpleCommand { public override bool CanExecute(object parameter) diff --git a/ILSpy/Commands/ManageAssemblyListsCommand.cs b/ILSpy/Commands/ManageAssemblyListsCommand.cs index ec58ff59dd..0f2240e147 100644 --- a/ILSpy/Commands/ManageAssemblyListsCommand.cs +++ b/ILSpy/Commands/ManageAssemblyListsCommand.cs @@ -17,11 +17,14 @@ // DEALINGS IN THE SOFTWARE. +using System.ComponentModel.Composition; + using ICSharpCode.ILSpy.Properties; namespace ICSharpCode.ILSpy { [ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.ManageAssembly_Lists), MenuIcon = "Images/AssemblyList", MenuCategory = nameof(Resources.Open), MenuOrder = 1.7)] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class ManageAssemblyListsCommand : SimpleCommand { public override void Execute(object parameter) diff --git a/ILSpy/Commands/OpenCommand.cs b/ILSpy/Commands/OpenCommand.cs index a1663a4991..7c66ac43a8 100644 --- a/ILSpy/Commands/OpenCommand.cs +++ b/ILSpy/Commands/OpenCommand.cs @@ -16,6 +16,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System.ComponentModel.Composition; using System.Windows.Input; using ICSharpCode.ILSpy.Properties; @@ -24,6 +25,7 @@ namespace ICSharpCode.ILSpy { [ExportToolbarCommand(ToolTip = nameof(Resources.Open), ToolbarIcon = "Images/Open", ToolbarCategory = nameof(Resources.Open), ToolbarOrder = 0)] [ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources._Open), MenuIcon = "Images/Open", MenuCategory = nameof(Resources.Open), MenuOrder = 0)] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class OpenCommand : CommandWrapper { public OpenCommand() diff --git a/ILSpy/Commands/OpenFromGacCommand.cs b/ILSpy/Commands/OpenFromGacCommand.cs index a077ceb9d8..393e00eeb5 100644 --- a/ILSpy/Commands/OpenFromGacCommand.cs +++ b/ILSpy/Commands/OpenFromGacCommand.cs @@ -16,12 +16,15 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System.ComponentModel.Composition; + using ICSharpCode.ILSpy.AppEnv; using ICSharpCode.ILSpy.Properties; namespace ICSharpCode.ILSpy { [ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.OpenFrom_GAC), MenuIcon = "Images/AssemblyListGAC", MenuCategory = nameof(Resources.Open), MenuOrder = 1)] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class OpenFromGacCommand : SimpleCommand { public override bool CanExecute(object parameter) diff --git a/ILSpy/Commands/Pdb2XmlCommand.cs b/ILSpy/Commands/Pdb2XmlCommand.cs index f64478feb8..c697773704 100644 --- a/ILSpy/Commands/Pdb2XmlCommand.cs +++ b/ILSpy/Commands/Pdb2XmlCommand.cs @@ -19,6 +19,7 @@ #if DEBUG using System.Collections.Generic; +using System.ComponentModel.Composition; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -34,6 +35,7 @@ namespace ICSharpCode.ILSpy { [ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.DEBUGDumpPDBAsXML), MenuCategory = nameof(Resources.Open), MenuOrder = 2.6)] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class Pdb2XmlCommand : SimpleCommand { public override bool CanExecute(object parameter) @@ -70,6 +72,7 @@ internal static void Execute(IEnumerable nodes) } [ExportContextMenuEntry(Header = nameof(Resources.DEBUGDumpPDBAsXML))] + [PartCreationPolicy(CreationPolicy.Shared)] class Pdb2XmlCommandContextMenuEntry : IContextMenuEntry { public void Execute(TextViewContext context) diff --git a/ILSpy/Commands/RefreshCommand.cs b/ILSpy/Commands/RefreshCommand.cs index 3e188a7e62..cfb03bf34c 100644 --- a/ILSpy/Commands/RefreshCommand.cs +++ b/ILSpy/Commands/RefreshCommand.cs @@ -16,6 +16,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System.ComponentModel.Composition; using System.Windows.Input; using ICSharpCode.ILSpy.Properties; @@ -24,6 +25,7 @@ namespace ICSharpCode.ILSpy { [ExportToolbarCommand(ToolTip = nameof(Resources.RefreshCommand_ReloadAssemblies), ToolbarIcon = "Images/Refresh", ToolbarCategory = nameof(Resources.Open), ToolbarOrder = 2)] [ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources._Reload), MenuIcon = "Images/Refresh", MenuCategory = nameof(Resources.Open), MenuOrder = 2)] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class RefreshCommand : CommandWrapper { public RefreshCommand() diff --git a/ILSpy/Commands/RemoveAssembliesWithLoadErrors.cs b/ILSpy/Commands/RemoveAssembliesWithLoadErrors.cs index a2a4430be0..c8696104bd 100644 --- a/ILSpy/Commands/RemoveAssembliesWithLoadErrors.cs +++ b/ILSpy/Commands/RemoveAssembliesWithLoadErrors.cs @@ -16,6 +16,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System.ComponentModel.Composition; using System.Linq; using ICSharpCode.ILSpy.Properties; @@ -23,6 +24,7 @@ namespace ICSharpCode.ILSpy { [ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources._RemoveAssembliesWithLoadErrors), MenuCategory = nameof(Resources.Remove), MenuOrder = 2.6)] + [PartCreationPolicy(CreationPolicy.Shared)] class RemoveAssembliesWithLoadErrors : SimpleCommand { public override bool CanExecute(object parameter) @@ -44,6 +46,7 @@ public override void Execute(object parameter) } [ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.ClearAssemblyList), MenuCategory = nameof(Resources.Remove), MenuOrder = 2.6)] + [PartCreationPolicy(CreationPolicy.Shared)] class ClearAssemblyList : SimpleCommand { public override bool CanExecute(object parameter) diff --git a/ILSpy/Commands/SaveCodeContextMenuEntry.cs b/ILSpy/Commands/SaveCodeContextMenuEntry.cs index 5ce79ee102..d7e38ada3d 100644 --- a/ILSpy/Commands/SaveCodeContextMenuEntry.cs +++ b/ILSpy/Commands/SaveCodeContextMenuEntry.cs @@ -30,10 +30,14 @@ using Microsoft.Win32; using ICSharpCode.ILSpyX.TreeView; +using System.ComponentModel.Composition; + +using ICSharpCode.ILSpy.Util; namespace ICSharpCode.ILSpy.TextView { [ExportContextMenuEntry(Header = nameof(Resources._SaveCode), Category = nameof(Resources.Save), Icon = "Images/Save")] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class SaveCodeContextMenuEntry : IContextMenuEntry { public void Execute(TextViewContext context) @@ -58,7 +62,7 @@ public static bool CanExecute(IReadOnlyList selectedNodes) public static void Execute(IReadOnlyList selectedNodes) { - var currentLanguage = MainWindow.Instance.CurrentLanguage; + var currentLanguage = SettingsService.Instance.SessionSettings.LanguageSettings.Language; var tabPage = Docking.DockWorkspace.Instance.ActiveTabPage; tabPage.ShowTextView(textView => { if (selectedNodes.Count == 1 && selectedNodes[0] is ILSpyTreeNode singleSelection) diff --git a/ILSpy/Commands/SaveCommand.cs b/ILSpy/Commands/SaveCommand.cs index 9f07f518fd..8b8871e0a8 100644 --- a/ILSpy/Commands/SaveCommand.cs +++ b/ILSpy/Commands/SaveCommand.cs @@ -16,6 +16,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System.ComponentModel.Composition; using System.Windows.Input; using ICSharpCode.ILSpy.Properties; @@ -23,6 +24,7 @@ namespace ICSharpCode.ILSpy { [ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources._SaveCode), MenuIcon = "Images/Save", MenuCategory = nameof(Resources.Save), MenuOrder = 0)] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class SaveCommand : CommandWrapper { public SaveCommand() diff --git a/ILSpy/Commands/ScopeSearchToAssembly.cs b/ILSpy/Commands/ScopeSearchToAssembly.cs index 1ec6ef73ba..3d8a103971 100644 --- a/ILSpy/Commands/ScopeSearchToAssembly.cs +++ b/ILSpy/Commands/ScopeSearchToAssembly.cs @@ -18,6 +18,7 @@ #nullable enable using System; +using System.ComponentModel.Composition; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.AppEnv; @@ -27,6 +28,7 @@ namespace ICSharpCode.ILSpy { [ExportContextMenuEntry(Header = nameof(Resources.ScopeSearchToThisAssembly), Category = nameof(Resources.Analyze), Order = 9999)] + [PartCreationPolicy(CreationPolicy.Shared)] public class ScopeSearchToAssembly : IContextMenuEntry { public void Execute(TextViewContext context) diff --git a/ILSpy/Commands/ScopeSearchToNamespace.cs b/ILSpy/Commands/ScopeSearchToNamespace.cs index 11a411ed6f..c6be0a9db7 100644 --- a/ILSpy/Commands/ScopeSearchToNamespace.cs +++ b/ILSpy/Commands/ScopeSearchToNamespace.cs @@ -16,6 +16,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. using System; +using System.ComponentModel.Composition; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.AppEnv; @@ -25,6 +26,7 @@ namespace ICSharpCode.ILSpy { [ExportContextMenuEntry(Header = nameof(Resources.ScopeSearchToThisNamespace), Category = nameof(Resources.Analyze), Order = 9999)] + [PartCreationPolicy(CreationPolicy.Shared)] public class ScopeSearchToNamespace : IContextMenuEntry { public void Execute(TextViewContext context) diff --git a/ILSpy/Commands/SearchMsdnContextMenuEntry.cs b/ILSpy/Commands/SearchMsdnContextMenuEntry.cs index 08c7f5a425..a49f1c456a 100644 --- a/ILSpy/Commands/SearchMsdnContextMenuEntry.cs +++ b/ILSpy/Commands/SearchMsdnContextMenuEntry.cs @@ -23,9 +23,12 @@ using ICSharpCode.ILSpy.TreeNodes; namespace ICSharpCode.ILSpy { + using System.ComponentModel.Composition; + using ICSharpCode.Decompiler.TypeSystem; [ExportContextMenuEntry(Header = nameof(Resources.SearchMSDN), Icon = "images/SearchMsdn", Order = 9999)] + [PartCreationPolicy(CreationPolicy.Shared)] internal sealed class SearchMsdnContextMenuEntry : IContextMenuEntry { private static string msdnAddress = "https://docs.microsoft.com/dotnet/api/{0}"; diff --git a/ILSpy/Commands/SelectPdbContextMenuEntry.cs b/ILSpy/Commands/SelectPdbContextMenuEntry.cs index 1d8da975f8..061ff7e6b0 100644 --- a/ILSpy/Commands/SelectPdbContextMenuEntry.cs +++ b/ILSpy/Commands/SelectPdbContextMenuEntry.cs @@ -16,6 +16,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System.ComponentModel.Composition; using System.IO; using System.Linq; @@ -27,6 +28,7 @@ namespace ICSharpCode.ILSpy { [ExportContextMenuEntry(Header = nameof(Resources.SelectPDB))] + [PartCreationPolicy(CreationPolicy.Shared)] class SelectPdbContextMenuEntry : IContextMenuEntry { public async void Execute(TextViewContext context) diff --git a/ILSpy/Commands/SetThemeCommand.cs b/ILSpy/Commands/SetThemeCommand.cs index 9083300ab4..2d1a0b32ac 100644 --- a/ILSpy/Commands/SetThemeCommand.cs +++ b/ILSpy/Commands/SetThemeCommand.cs @@ -1,11 +1,13 @@ -namespace ICSharpCode.ILSpy.Commands +using ICSharpCode.ILSpy.Util; + +namespace ICSharpCode.ILSpy.Commands { public class SetThemeCommand : SimpleCommand { public override void Execute(object parameter) { if (parameter is string theme) - MainWindow.Instance.SessionSettings.Theme = theme; + SettingsService.Instance.SessionSettings.Theme = theme; } } } diff --git a/ILSpy/Commands/ShowCFGContextMenuEntry.cs b/ILSpy/Commands/ShowCFGContextMenuEntry.cs index a3e2b4e299..b4e8be003e 100644 --- a/ILSpy/Commands/ShowCFGContextMenuEntry.cs +++ b/ILSpy/Commands/ShowCFGContextMenuEntry.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.Composition; using System.Windows; using ICSharpCode.Decompiler.FlowAnalysis; @@ -11,6 +12,7 @@ namespace ICSharpCode.ILSpy.Commands { #if DEBUG [ExportContextMenuEntry(Header = "DEBUG -- Show CFG")] + [PartCreationPolicy(CreationPolicy.Shared)] internal class ShowCFGContextMenuEntry : IContextMenuEntry { public void Execute(TextViewContext context) diff --git a/ILSpy/Commands/SortAssemblyListCommand.cs b/ILSpy/Commands/SortAssemblyListCommand.cs index 110a60b8a9..7621778a77 100644 --- a/ILSpy/Commands/SortAssemblyListCommand.cs +++ b/ILSpy/Commands/SortAssemblyListCommand.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel.Composition; using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpyX; @@ -27,6 +28,7 @@ namespace ICSharpCode.ILSpy { [ExportMainMenuCommand(ParentMenuID = nameof(Resources._View), Header = nameof(Resources.SortAssembly_listName), MenuIcon = "Images/Sort", MenuCategory = nameof(Resources.View))] [ExportToolbarCommand(ToolTip = nameof(Resources.SortAssemblyListName), ToolbarIcon = "Images/Sort", ToolbarCategory = nameof(Resources.View))] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class SortAssemblyListCommand : SimpleCommand, IComparer { public override void Execute(object parameter) @@ -43,6 +45,7 @@ int IComparer.Compare(LoadedAssembly x, LoadedAssembly y) [ExportMainMenuCommand(ParentMenuID = nameof(Resources._View), Header = nameof(Resources._CollapseTreeNodes), MenuIcon = "Images/CollapseAll", MenuCategory = nameof(Resources.View))] [ExportToolbarCommand(ToolTip = nameof(Resources.CollapseTreeNodes), ToolbarIcon = "Images/CollapseAll", ToolbarCategory = nameof(Resources.View))] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class CollapseAllCommand : SimpleCommand { public override void Execute(object parameter) diff --git a/ILSpy/ContextMenuEntry.cs b/ILSpy/ContextMenuEntry.cs index 9ce4416a2a..fcbdb1bae9 100644 --- a/ILSpy/ContextMenuEntry.cs +++ b/ILSpy/ContextMenuEntry.cs @@ -31,6 +31,8 @@ using ICSharpCode.ILSpy.Controls.TreeView; using ICSharpCode.ILSpyX.TreeView; +using TomsToolbox.Composition; + namespace ICSharpCode.ILSpy { public interface IContextMenuEntry @@ -205,7 +207,7 @@ public static void Add(DataGrid dataGrid) readonly DecompilerTextView textView; readonly ListBox listBox; readonly DataGrid dataGrid; - readonly Lazy[] entries; + readonly IExport[] entries; private ContextMenuProvider() { @@ -288,8 +290,8 @@ void dataGrid_ContextMenuOpening(object sender, ContextMenuEventArgs e) bool ShowContextMenu(TextViewContext context, out ContextMenu menu) { menu = new ContextMenu(); - var menuGroups = new Dictionary[]>(); - Lazy[] topLevelGroup = null; + var menuGroups = new Dictionary[]>(); + IExport[] topLevelGroup = null; foreach (var group in entries.OrderBy(c => c.Metadata.Order).GroupBy(c => c.Metadata.ParentMenuID)) { if (group.Key == null) @@ -301,10 +303,10 @@ bool ShowContextMenu(TextViewContext context, out ContextMenu menu) menuGroups.Add(group.Key, group.ToArray()); } } - BuildMenu(topLevelGroup ?? Array.Empty>(), menu.Items); + BuildMenu(topLevelGroup ?? Array.Empty>(), menu.Items); return menu.Items.Count > 0; - void BuildMenu(Lazy[] menuGroup, ItemCollection parent) + void BuildMenu(IExport[] menuGroup, ItemCollection parent) { foreach (var category in menuGroup.GroupBy(c => c.Metadata.Category)) { diff --git a/ILSpy/Controls/BoolToVisibilityConverter.cs b/ILSpy/Controls/BoolToVisibilityConverter.cs deleted file mode 100644 index 31211050c2..0000000000 --- a/ILSpy/Controls/BoolToVisibilityConverter.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 2018 Siegfried Pammer -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this -// software and associated documentation files (the "Software"), to deal in the Software -// without restriction, including without limitation the rights to use, copy, modify, merge, -// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons -// to whom the Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE - -using System; -using System.Globalization; -using System.Windows; -using System.Windows.Data; - -namespace ICSharpCode.ILSpy.Controls -{ - public class BoolToVisibilityConverter : IValueConverter - { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - if (!(parameter is Visibility notVisible)) - notVisible = Visibility.Collapsed; - if (!(value is bool b)) - return notVisible; - return b ? Visibility.Visible : notVisible; - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - if (!(value is Visibility visibility)) - return false; - return visibility == Visibility.Visible; - } - } -} diff --git a/ILSpy/Controls/TreeView/SharpTreeView.cs b/ILSpy/Controls/TreeView/SharpTreeView.cs index 3d4e3e7e60..faa7cb2f05 100644 --- a/ILSpy/Controls/TreeView/SharpTreeView.cs +++ b/ILSpy/Controls/TreeView/SharpTreeView.cs @@ -54,7 +54,7 @@ static SharpTreeView() RegisterCommands(); } - public static ResourceKey DefaultItemContainerStyleKey { get; private set; } + public static ResourceKey DefaultItemContainerStyleKey { get; } public SharpTreeView() { @@ -62,7 +62,7 @@ public SharpTreeView() } public static readonly DependencyProperty RootProperty = - DependencyProperty.Register("Root", typeof(SharpTreeNode), typeof(SharpTreeView)); + DependencyProperty.Register(nameof(Root), typeof(SharpTreeNode), typeof(SharpTreeView)); public SharpTreeNode Root { get { return (SharpTreeNode)GetValue(RootProperty); } @@ -70,7 +70,7 @@ public SharpTreeNode Root { } public static readonly DependencyProperty ShowRootProperty = - DependencyProperty.Register("ShowRoot", typeof(bool), typeof(SharpTreeView), + DependencyProperty.Register(nameof(ShowRoot), typeof(bool), typeof(SharpTreeView), new FrameworkPropertyMetadata(true)); public bool ShowRoot { @@ -79,7 +79,7 @@ public bool ShowRoot { } public static readonly DependencyProperty ShowRootExpanderProperty = - DependencyProperty.Register("ShowRootExpander", typeof(bool), typeof(SharpTreeView), + DependencyProperty.Register(nameof(ShowRootExpander), typeof(bool), typeof(SharpTreeView), new FrameworkPropertyMetadata(false)); public bool ShowRootExpander { @@ -88,7 +88,7 @@ public bool ShowRootExpander { } public static readonly DependencyProperty AllowDropOrderProperty = - DependencyProperty.Register("AllowDropOrder", typeof(bool), typeof(SharpTreeView)); + DependencyProperty.Register(nameof(AllowDropOrder), typeof(bool), typeof(SharpTreeView)); public bool AllowDropOrder { get { return (bool)GetValue(AllowDropOrderProperty); } @@ -96,7 +96,7 @@ public bool AllowDropOrder { } public static readonly DependencyProperty ShowLinesProperty = - DependencyProperty.Register("ShowLines", typeof(bool), typeof(SharpTreeView), + DependencyProperty.Register(nameof(ShowLines), typeof(bool), typeof(SharpTreeView), new FrameworkPropertyMetadata(true)); public bool ShowLines { diff --git a/ILSpy/Docking/CloseAllDocumentsCommand.cs b/ILSpy/Docking/CloseAllDocumentsCommand.cs index fc33eb8cd6..2c0ceb41be 100644 --- a/ILSpy/Docking/CloseAllDocumentsCommand.cs +++ b/ILSpy/Docking/CloseAllDocumentsCommand.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.Composition; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -9,6 +10,7 @@ namespace ICSharpCode.ILSpy.Docking { [ExportMainMenuCommand(Header = nameof(Resources.Window_CloseAllDocuments), ParentMenuID = nameof(Resources._Window))] + [PartCreationPolicy(CreationPolicy.Shared)] class CloseAllDocumentsCommand : SimpleCommand { public override void Execute(object parameter) @@ -18,6 +20,7 @@ public override void Execute(object parameter) } [ExportMainMenuCommand(Header = nameof(Resources.Window_ResetLayout), ParentMenuID = nameof(Resources._Window))] + [PartCreationPolicy(CreationPolicy.Shared)] class ResetLayoutCommand : SimpleCommand { public override void Execute(object parameter) diff --git a/ILSpy/Docking/DockWorkspace.cs b/ILSpy/Docking/DockWorkspace.cs index 2b1241e65f..6ae7a0bdce 100644 --- a/ILSpy/Docking/DockWorkspace.cs +++ b/ILSpy/Docking/DockWorkspace.cs @@ -35,27 +35,28 @@ using AvalonDock.Layout.Serialization; using ICSharpCode.AvalonEdit.Highlighting; +using ICSharpCode.ILSpy.Analyzers; using ICSharpCode.ILSpy.TextView; +using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpy.ViewModels; +using TomsToolbox.Wpf; + namespace ICSharpCode.ILSpy.Docking { - public class DockWorkspace : INotifyPropertyChanged, ILayoutUpdateStrategy + public class DockWorkspace : ObservableObject, ILayoutUpdateStrategy { - private SessionSettings sessionSettings; - - public event PropertyChangedEventHandler PropertyChanged; + private static SessionSettings SessionSettings => SettingsService.Instance.SessionSettings; - public static DockWorkspace Instance { get; private set; } + public static readonly DockWorkspace Instance = new(); - internal DockWorkspace(MainWindow parent) + private DockWorkspace() { - Instance = this; this.TabPages.CollectionChanged += Documents_CollectionChanged; - parent.CurrentAssemblyListChanged += MainWindow_Instance_CurrentAssemblyListChanged; + MessageBus.Subscribers += (sender, e) => CurrentAssemblyList_Changed(sender, e); } - private void MainWindow_Instance_CurrentAssemblyListChanged(object sender, NotifyCollectionChangedEventArgs e) + private void CurrentAssemblyList_Changed(object sender, NotifyCollectionChangedEventArgs e) { if (e.OldItems == null) { @@ -88,6 +89,11 @@ private void MainWindow_Instance_CurrentAssemblyListChanged(object sender, Notif private void Documents_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { var collection = (PaneCollection)sender; + if (e.Action == NotifyCollectionChangedAction.Add) + { + ActiveTabPage = e.NewItems?[0] as TabPageModel; + } + bool canClose = collection.Count > 1; foreach (var item in collection) { @@ -118,31 +124,31 @@ public void Remove(PaneModel model) tool.IsVisible = false; } - private TabPageModel _activeTabPage = null; + private TabPageModel activeTabPage = null; public TabPageModel ActiveTabPage { get { - return _activeTabPage; + return activeTabPage; } set { - if (_activeTabPage != value) + if (!SetProperty(ref activeTabPage, value)) { - _activeTabPage = value; - var state = value.GetState(); - if (state != null) + return; + } + + var state = value.GetState(); + if (state != null) + { + if (state.DecompiledNodes != null) { - if (state.DecompiledNodes != null) - { - MainWindow.Instance.SelectNodes(state.DecompiledNodes, - inNewTabPage: false, setFocus: true, changingActiveTab: true); - } - else - { - MainWindow.Instance.NavigateTo(new RequestNavigateEventArgs(state.ViewedUri, null)); - } + MainWindow.Instance.SelectNodes(state.DecompiledNodes, + inNewTabPage: false, setFocus: true, changingActiveTab: true); + } + else + { + MainWindow.Instance.NavigateTo(new(state.ViewedUri, null)); } - - RaisePropertyChanged(nameof(ActiveTabPage)); } + MessageBus.Send(this, new DockWorkspaceActiveTabPageChangedEventArgs()); } } @@ -153,7 +159,7 @@ public void InitializeLayout(DockingManager manager) serializer.LayoutSerializationCallback += LayoutSerializationCallback; try { - sessionSettings.DockLayout.Deserialize(serializer); + SessionSettings.DockLayout.Deserialize(serializer); } finally { @@ -181,11 +187,6 @@ void LayoutSerializationCallback(object sender, LayoutSerializationCallbackEvent } } - protected void RaisePropertyChanged([CallerMemberName] string propertyName = null) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - public void ShowText(AvalonEditTextOutput textOutput) { ActiveTabPage.ShowTextView(textView => textView.ShowText(textOutput)); @@ -201,11 +202,6 @@ internal void ShowNodes(AvalonEditTextOutput output, TreeNodes.ILSpyTreeNode[] n ActiveTabPage.ShowTextView(textView => textView.ShowNodes(output, nodes, highlighting)); } - internal void LoadSettings(SessionSettings sessionSettings) - { - this.sessionSettings = sessionSettings; - } - internal void CloseAllTabs() { foreach (var doc in TabPages.ToArray()) @@ -222,7 +218,7 @@ internal void ResetLayout() pane.IsVisible = false; } CloseAllTabs(); - sessionSettings.DockLayout.Reset(); + SessionSettings.DockLayout.Reset(); InitializeLayout(MainWindow.Instance.DockManager); MainWindow.Instance.Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action)MainWindow.Instance.RefreshDecompiledView); } @@ -243,7 +239,7 @@ public bool BeforeInsertAnchorable(LayoutRoot layout, LayoutAnchorable anchorabl previousContainer.Children.Add(anchorableToShow); return true; case LegacyToolPaneLocation.Bottom: - previousContainer = GetContainer(); + previousContainer = GetContainer(); previousContainer.Children.Add(anchorableToShow); return true; default: diff --git a/ILSpy/Docking/PaneCollection.cs b/ILSpy/Docking/PaneCollection.cs index 5b58610625..72b1b851be 100644 --- a/ILSpy/Docking/PaneCollection.cs +++ b/ILSpy/Docking/PaneCollection.cs @@ -20,15 +20,13 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; -using System.ComponentModel; -using System.Linq; using ICSharpCode.ILSpy.ViewModels; namespace ICSharpCode.ILSpy.Docking { public class PaneCollection : INotifyCollectionChanged, ICollection - where T : PaneModel + where T : PaneModel, new() { private ObservableCollection observableCollection = new ObservableCollection(); @@ -39,8 +37,10 @@ public PaneCollection() observableCollection.CollectionChanged += (sender, e) => CollectionChanged?.Invoke(this, e); } - public void Add(T item) + public void Add(T item = null) { + item ??= new T(); + observableCollection.Add(item); item.IsVisible = true; diff --git a/ILSpy/Docking/ActiveTabPageConverter.cs b/ILSpy/Docking/TabPageGuardConverter.cs similarity index 73% rename from ILSpy/Docking/ActiveTabPageConverter.cs rename to ILSpy/Docking/TabPageGuardConverter.cs index 191a7088ad..b6c2d23615 100644 --- a/ILSpy/Docking/ActiveTabPageConverter.cs +++ b/ILSpy/Docking/TabPageGuardConverter.cs @@ -21,24 +21,20 @@ using ICSharpCode.ILSpy.ViewModels; +using TomsToolbox.Wpf.Converters; + namespace ICSharpCode.ILSpy.Docking { - public class ActiveTabPageConverter : IValueConverter + public class TabPageGuardConverter : ValueConverter { - public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + protected override object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { - if (value is TabPageModel) - return value; - - return Binding.DoNothing; + return value is TabPageModel ? value : Binding.DoNothing; } - public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + protected override object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { - if (value is TabPageModel) - return value; - - return Binding.DoNothing; + return value is TabPageModel ? value : Binding.DoNothing; } } } diff --git a/ILSpy/ExportProviderAdapter.cs b/ILSpy/ExportProviderAdapter.cs new file mode 100644 index 0000000000..81bc7bfbf1 --- /dev/null +++ b/ILSpy/ExportProviderAdapter.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; + +using Microsoft.VisualStudio.Composition; + +using TomsToolbox.Composition; +using TomsToolbox.Essentials; + +namespace ICSharpCode.ILSpy; + +#nullable enable + +/// +/// Adapter for Microsoft.VisualStudio.Composition. to . +/// +public sealed class ExportProviderAdapter : IExportProvider +{ + private static readonly Type DefaultMetadataType = typeof(Dictionary); + + private readonly ExportProvider _exportProvider; + + /// + /// Initializes a new instance of the class. + /// + /// The export provider. + public ExportProviderAdapter(ExportProvider exportProvider) + { + _exportProvider = exportProvider; + } + + event EventHandler? IExportProvider.ExportsChanged { add { } remove { } } + + T IExportProvider.GetExportedValue(string? contractName) where T : class + { + return _exportProvider.GetExportedValue(contractName) ?? throw new InvalidOperationException($"No export found for type {typeof(T).FullName} with contract '{contractName}'"); + } + + T? IExportProvider.GetExportedValueOrDefault(string? contractName) where T : class + { + return _exportProvider.GetExportedValues(contractName).SingleOrDefault(); + } + + bool IExportProvider.TryGetExportedValue(string? contractName, [NotNullWhen(true)] out T? value) where T : class + { + value = _exportProvider.GetExportedValues(contractName).SingleOrDefault(); + + return !Equals(value, default(T)); + } + + IEnumerable IExportProvider.GetExportedValues(string? contractName) where T : class + { + return _exportProvider.GetExportedValues(contractName); + } + + IEnumerable IExportProvider.GetExportedValues(Type contractType, string? contractName) + { + return _exportProvider + .GetExports(contractType, DefaultMetadataType, contractName) + .Select(item => item.Value) + .ExceptNullItems(); + } + + IEnumerable> IExportProvider.GetExports(Type contractType, string? contractName) + { + return _exportProvider + .GetExports(contractType, DefaultMetadataType, contractName) + .Select(item => new ExportAdapter(() => item.Value, new MetadataAdapter((IDictionary)item.Metadata))); + } + + IEnumerable> IExportProvider.GetExports(string? contractName) where T : class + { + return _exportProvider + .GetExports(typeof(T), DefaultMetadataType, contractName) + .Select(item => new ExportAdapter(() => (T?)item.Value, new MetadataAdapter((IDictionary)item.Metadata))); + } + + IEnumerable> IExportProvider.GetExports(string? contractName) where T : class where TMetadataView : class + { + return _exportProvider + .GetExports(contractName) + .Select(item => new ExportAdapter(() => item.Value, item.Metadata)); + } +} \ No newline at end of file diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index 7b83970f6f..36a8e7b443 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -50,6 +50,8 @@ + + @@ -90,8 +92,8 @@ - - + + diff --git a/ILSpy/FilterSettings.cs b/ILSpy/LanguageSettings.cs similarity index 79% rename from ILSpy/FilterSettings.cs rename to ILSpy/LanguageSettings.cs index 6adb3200db..ef7613ccfc 100644 --- a/ILSpy/FilterSettings.cs +++ b/ILSpy/LanguageSettings.cs @@ -23,6 +23,7 @@ using System.Runtime.CompilerServices; using System.Xml.Linq; +using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX; namespace ICSharpCode.ILSpy @@ -30,12 +31,7 @@ namespace ICSharpCode.ILSpy /// /// Represents the filters applied to the tree view. /// - /// - /// This class is mutable; but the ILSpyTreeNode filtering assumes that filter settings are immutable. - /// Thus, the main window will use one mutable instance (for data-binding), and will assign a new - /// clone to the ILSpyTreeNodes whenever the main mutable instance changes. - /// - public class FilterSettings : INotifyPropertyChanged + public class LanguageSettings : INotifyPropertyChanged { /// /// This dictionary is necessary to remember language versions across language changes. For example, @@ -44,13 +40,12 @@ public class FilterSettings : INotifyPropertyChanged /// private readonly Dictionary languageVersionHistory = new Dictionary(); - public FilterSettings(XElement element) + public LanguageSettings(XElement element) { this.ShowApiLevel = (ApiVisibility?)(int?)element.Element("ShowAPILevel") ?? ApiVisibility.PublicAndInternal; - this.Language = Languages.GetLanguage((string)element.Element("Language")); - this.LanguageVersion = Language.LanguageVersions.FirstOrDefault(v => v.Version == (string)element.Element("LanguageVersion")); - if (this.LanguageVersion == default(LanguageVersion)) - this.LanguageVersion = language.LanguageVersions.LastOrDefault(); + this.Language = Languages.GetLanguage((string)element.Element("Language")) ?? Languages.AllLanguages.First(); + this.LanguageVersion = Language.LanguageVersions.FirstOrDefault(v => v.Version == (string)element.Element("LanguageVersion")) + ?? Language.LanguageVersions.LastOrDefault(); } public XElement SaveAsXml() @@ -63,33 +58,6 @@ public XElement SaveAsXml() ); } - string searchTerm; - - /// - /// Gets/Sets the search term. - /// Only tree nodes containing the search term will be shown. - /// - public string SearchTerm { - get { return searchTerm; } - set { - if (searchTerm != value) - { - searchTerm = value; - OnPropertyChanged(nameof(SearchTerm)); - } - } - } - - /// - /// Gets whether a node with the specified text is matched by the current search term. - /// - public virtual bool SearchTermMatches(string text) - { - if (string.IsNullOrEmpty(searchTerm)) - return true; - return text.IndexOf(searchTerm, StringComparison.OrdinalIgnoreCase) >= 0; - } - ApiVisibility showApiLevel; /// @@ -211,15 +179,23 @@ protected virtual void OnPropertyChanged([CallerMemberName] string propertyName { if (PropertyChanged != null) { - PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); + var args = new PropertyChangedEventArgs(propertyName); + + PropertyChanged(this, args); + MessageBus.Send(this, new LanguageSettingsChangedEventArgs(args)); } } - public FilterSettings Clone() + + // This class has been initially called FilterSettings, but then has been Hijacked to store language settings as well. + // While the filter settings were some sort of local, the language settings are global. This is a bit of a mess. + // There has been a lot of workarounds cloning the FilterSettings to pass them down to the tree nodes, without messing up the global language settings. + // Finally, this filtering was not used at all, so this SearchTerm is just a placeholder to make the filtering code compile, in case someone wants to reactivate filtering in the future. + public string SearchTerm => string.Empty; + + public bool SearchTermMatches(string value) { - FilterSettings f = (FilterSettings)MemberwiseClone(); - f.PropertyChanged = null; - return f; + return true; } } } diff --git a/ILSpy/Languages/CSharpILMixedLanguage.cs b/ILSpy/Languages/CSharpILMixedLanguage.cs index 6ad3bb54e3..31975cc247 100644 --- a/ILSpy/Languages/CSharpILMixedLanguage.cs +++ b/ILSpy/Languages/CSharpILMixedLanguage.cs @@ -42,6 +42,7 @@ namespace ICSharpCode.ILSpy using SequencePoint = ICSharpCode.Decompiler.DebugInfo.SequencePoint; [Export(typeof(Language))] + [PartCreationPolicy(CreationPolicy.Shared)] class CSharpILMixedLanguage : ILLanguage { public override string Name => "IL with C#"; diff --git a/ILSpy/Languages/CSharpLanguage.cs b/ILSpy/Languages/CSharpLanguage.cs index 6e82962418..dcf7e91a2c 100644 --- a/ILSpy/Languages/CSharpLanguage.cs +++ b/ILSpy/Languages/CSharpLanguage.cs @@ -53,6 +53,7 @@ namespace ICSharpCode.ILSpy /// please directly use the CSharpDecompiler class. /// [Export(typeof(Language))] + [PartCreationPolicy(CreationPolicy.Shared)] public class CSharpLanguage : Language { string name = "C#"; diff --git a/ILSpy/Languages/ILLanguage.cs b/ILSpy/Languages/ILLanguage.cs index 95c881bb40..ab8f89ab7a 100644 --- a/ILSpy/Languages/ILLanguage.cs +++ b/ILSpy/Languages/ILLanguage.cs @@ -42,6 +42,7 @@ namespace ICSharpCode.ILSpy /// flat IL (detectControlStructure=false) and structured IL (detectControlStructure=true). /// [Export(typeof(Language))] + [PartCreationPolicy(CreationPolicy.Shared)] public class ILLanguage : Language { protected bool detectControlStructure = true; diff --git a/ILSpy/Languages/Languages.cs b/ILSpy/Languages/Languages.cs index 5721f0e728..a58df78da1 100644 --- a/ILSpy/Languages/Languages.cs +++ b/ILSpy/Languages/Languages.cs @@ -16,39 +16,31 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -using System.Collections.Generic; +using System; using System.Collections.ObjectModel; using System.Linq; -using Microsoft.VisualStudio.Composition; +using TomsToolbox.Composition; namespace ICSharpCode.ILSpy { public static class Languages { - // Start with a dummy list with an IL entry so that crashes - // in Initialize() (e.g. due to invalid plugins) don't lead to - // confusing follow-up errors in GetLanguage(). - private static ReadOnlyCollection allLanguages = new ReadOnlyCollection( - new Language[] { new ILLanguage() }); - /// /// A list of all languages. /// - public static ReadOnlyCollection AllLanguages { - get { return allLanguages; } - } + public static ReadOnlyCollection AllLanguages { get; } = Initialize(App.ExportProvider); - internal static void Initialize(ExportProvider ep) + static ReadOnlyCollection Initialize(IExportProvider ep) { - List languages = new List(); - languages.AddRange(ep.GetExportedValues()); - languages.Sort((a, b) => a.Name.CompareTo(b.Name)); + var languages = ep.GetExportedValues().ToList(); + + languages.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.Ordinal)); #if DEBUG languages.AddRange(ILAstLanguage.GetDebugLanguages()); languages.AddRange(CSharpLanguage.GetDebugLanguages()); #endif - allLanguages = languages.AsReadOnly(); + return languages.AsReadOnly(); } /// diff --git a/ILSpy/MainWindow.xaml b/ILSpy/MainWindow.xaml index 6f423f315a..a6650e05aa 100644 --- a/ILSpy/MainWindow.xaml +++ b/ILSpy/MainWindow.xaml @@ -2,8 +2,8 @@ - - -