From 3fb153940b169637b4d4028c4eb85ff9e76ba692 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 21 Jul 2024 23:08:27 +0200 Subject: [PATCH 01/35] AssignVariableNames: Handle fixed statement special cases --- .../ICSharpCode.Decompiler.Tests.csproj | 2 ++ .../TestCases/ILPretty/MonoFixed.cs | 8 ++++---- .../TestCases/Pretty/UnsafeCode.cs | 4 ++-- .../IL/Transforms/AssignVariableNames.cs | 13 +++++++++++++ 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index 697ca18d9..f1b1e5777 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -93,6 +93,7 @@ + @@ -128,6 +129,7 @@ + diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/MonoFixed.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/MonoFixed.cs index f3ad7c389..c420c9d41 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/MonoFixed.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/MonoFixed.cs @@ -6,13 +6,13 @@ public class MonoFixed { fixed (char* ptr = text) { - fixed (char* ptr2 = Environment.UserName) + fixed (char* userName = Environment.UserName) { - fixed (char* ptr3 = text) + fixed (char* ptr2 = text) { *ptr = 'c'; - *ptr2 = 'd'; - *ptr3 = 'e'; + *userName = 'd'; + *ptr2 = 'e'; } } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs index 9901fd69e..3b993096e 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs @@ -431,9 +431,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public unsafe void PinFixedMember(ref StructWithFixedSizeMembers m) { - fixed (int* ptr = m.Integers) + fixed (int* integers = m.Integers) { - UsePointer(ptr); + UsePointer(integers); } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs index d84091d9d..108b1aa80 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs @@ -326,6 +326,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms { switch (arg) { + case GetPinnableReference _: case LdObj _: case LdFlda _: case LdsFlda _: @@ -431,6 +432,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (!currentLowerCaseTypeOrMemberNames.Contains(name)) proposedNameForStores.Add(name); } + else if (store is PinnedRegion pinnedRegion) + { + var name = GetNameFromInstruction(pinnedRegion.Init); + if (!currentLowerCaseTypeOrMemberNames.Contains(name)) + proposedNameForStores.Add(name); + } } if (proposedNameForStores.Count == 1) { @@ -485,9 +492,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms { switch (inst) { + case GetPinnableReference getPinnableReference: + return GetNameFromInstruction(getPinnableReference.Argument); case LdObj ldobj: return GetNameFromInstruction(ldobj.Target); case LdFlda ldflda: + if (ldflda.Field.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) + return GetNameFromInstruction(ldflda.Target); return CleanUpVariableName(ldflda.Field.Name); case LdsFlda ldsflda: return CleanUpVariableName(ldsflda.Field.Name); @@ -567,6 +578,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; if (m.Name == "Concat" && m.DeclaringType.IsKnownType(KnownTypeCode.String)) return true; + if (m.Name == "GetPinnableReference") + return true; return false; } From c67d086e2fa670532bc161bd3fa11177d1481520 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 21 Jul 2024 23:22:32 +0200 Subject: [PATCH 02/35] Add test cases for naming conflicts related to foreach, using and fixed variables. Remove unnecessary ConflictWithLocal check in AssignVariableNames. --- .../Pretty/VariableNamingWithoutSymbols.cs | 54 ++++++++++++++++++- .../IL/Transforms/AssignVariableNames.cs | 12 +---- 2 files changed, 54 insertions(+), 12 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/VariableNamingWithoutSymbols.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/VariableNamingWithoutSymbols.cs index e724559fd..bb458845b 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/VariableNamingWithoutSymbols.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/VariableNamingWithoutSymbols.cs @@ -1,4 +1,7 @@ -namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty +using System; +using System.Collections.Generic; + +namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { internal class VariableNamingWithoutSymbols { @@ -25,5 +28,54 @@ string text2 = c.Text; #endif } + + private static IDisposable GetData() + { + return null; + } + + private static void UseData(IDisposable data) + { + + } + + private static IEnumerable GetItems() + { + throw null; + } + + private static byte[] GetMemory() + { + throw null; + } + + private static void Test(int item) + { + foreach (int item2 in GetItems()) + { + Console.WriteLine(item2); + } + } + + private static void Test(IDisposable data) + { +#if CS80 + using IDisposable data2 = GetData(); + UseData(data2); +#else + using (IDisposable data2 = GetData()) + { + UseData(data2); + } +#endif + } + + private unsafe static void Test(byte[] memory) + { + fixed (byte* memory2 = GetMemory()) + { + Console.WriteLine(*memory2); + } + } } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs index 108b1aa80..4b9dcc5dd 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs @@ -233,7 +233,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms void AssignName() { - if (v.HasGeneratedName || !IsValidName(v.Name) || ConflictWithLocal(v)) + if (v.HasGeneratedName || !IsValidName(v.Name)) { // don't use the name from the debug symbols if it looks like a generated name v.Name = null; @@ -337,16 +337,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } - bool ConflictWithLocal(ILVariable v) - { - if (v.Kind == VariableKind.UsingLocal || v.Kind == VariableKind.ForeachLocal) - { - if (reservedVariableNames.ContainsKey(v.Name)) - return true; - } - return false; - } - internal static bool IsValidName(string varName) { if (string.IsNullOrWhiteSpace(varName)) From 2ca5b5affe413c9d07070e1d00c9e62d382eef02 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 27 Jul 2024 13:35:20 +0200 Subject: [PATCH 03/35] Rewrite AssignVariableNames algorithm to use variable usages instead of the list of variables. --- .../TestCases/Pretty/Async.cs | 12 +- .../TestCases/Pretty/DelegateConstruction.cs | 16 +- .../TestCases/Pretty/LocalFunctions.cs | 28 +-- .../Pretty/VariableNamingWithoutSymbols.cs | 8 + ...eScalarReplacementOfAggregates.Expected.cs | 12 +- .../IL/Transforms/AssignVariableNames.cs | 215 ++++++++---------- .../IL/Transforms/ExpressionTransforms.cs | 2 + 7 files changed, 143 insertions(+), 150 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs index 7e918263d..9ac730b3f 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs @@ -250,7 +250,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { throw null; } - catch (Exception ex2) when (i == 0) + catch (Exception ex) when (i == 0) { Console.WriteLine("First!"); if (i == 1) @@ -258,9 +258,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty throw; } await Task.Yield(); - Console.WriteLine(ex2.StackTrace); + Console.WriteLine(ex.StackTrace); } - catch (Exception ex3) when (True()) + catch (Exception ex2) when (True()) { Console.WriteLine("Second!"); if (i == 1) @@ -268,9 +268,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty throw; } await Task.Yield(); - Console.WriteLine(ex3.StackTrace); + Console.WriteLine(ex2.StackTrace); } - catch (Exception ex) + catch (Exception ex3) { Console.WriteLine("Third!"); if (i == 1) @@ -278,7 +278,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty throw; } await Task.Yield(); - Console.WriteLine(ex.StackTrace); + Console.WriteLine(ex3.StackTrace); } catch when (i == 0) { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs index 6fe579180..c87ca9a7d 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs @@ -353,19 +353,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction public static void NameConflict() { - // i is captured variable, - // j is parameter in anonymous method + // i is local in main method, + // j is captured variable, + // k is parameter in anonymous method // l is local in anonymous method, - // k is local in main method // Ensure that the decompiler doesn't introduce name conflicts List> list = new List>(); - for (int k = 0; k < 10; k++) + for (int i = 0; i < 10; i++) { - int i; - for (i = 0; i < 10; i++) + int j; + for (j = 0; j < 10; j++) { - list.Add(delegate (int j) { - for (int l = 0; l < i; l += j) + list.Add(delegate (int k) { + for (int l = 0; l < j; l += k) { Console.WriteLine(); } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs index 677f6ebaf..ff29a4351 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs @@ -39,10 +39,10 @@ namespace LocalFunctions #pragma warning disable CS0219 T2 t2 = default(T2); object z = this; - for (int j = 0; j < 10; j++) + for (int i = 0; i < 10; i++) { - int i = 0; - i += NonStaticMethod6(0); + int i2 = 0; + i2 += NonStaticMethod6(0); #if CS90 [My] [return: My] @@ -56,7 +56,7 @@ namespace LocalFunctions return NonStaticMethod6_1() + NonStaticMethod6_1() + z.GetHashCode(); int NonStaticMethod6_1() { - return i + l + NonStaticMethod6(0) + StaticMethod1(); + return i2 + l + NonStaticMethod6(0) + StaticMethod1(); } } } @@ -119,10 +119,10 @@ namespace LocalFunctions { T2 t2 = default(T2); object z = this; - for (int j = 0; j < 10; j++) + for (int i = 0; i < 10; i++) { - int i = 0; - i += StaticInvokeAsFunc(NonStaticMethod6); + int i2 = 0; + i2 += StaticInvokeAsFunc(NonStaticMethod6); int NonStaticMethod6() { t2 = default(T2); @@ -130,7 +130,7 @@ namespace LocalFunctions return StaticInvokeAsFunc(NonStaticMethod6_1) + StaticInvokeAsFunc(NonStaticMethod6_1) + z.GetHashCode(); int NonStaticMethod6_1() { - return i + l + StaticInvokeAsFunc(NonStaticMethod6) + StaticInvokeAsFunc(StaticMethod1); + return i2 + l + StaticInvokeAsFunc(NonStaticMethod6) + StaticInvokeAsFunc(StaticMethod1); } } } @@ -743,12 +743,12 @@ namespace LocalFunctions int ZZZ_1() { t0 = 0; - int t = t0; + int t3 = t0; return new Func(ZZZ_1_0)(); int ZZZ_1_0() { t0 = 0; - t = 0; + t3 = 0; return 0; } } @@ -779,14 +779,14 @@ namespace LocalFunctions int ZZZ_1() { t0 = 0; - int t1 = t0; + int t3 = t0; #if !OPT Func func = delegate { #else return ((Func)delegate { #endif t0 = 0; - t1 = 0; + t3 = 0; return 0; #if !OPT }; @@ -822,14 +822,14 @@ namespace LocalFunctions int ZZZ_1() { t0 = 0; - int t1 = t0; + int t3 = t0; #if !OPT Func func = delegate { #else return ((Func)delegate { #endif t0 = 0; - t1 = 0; + t3 = 0; return 0; #if !OPT }; diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/VariableNamingWithoutSymbols.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/VariableNamingWithoutSymbols.cs index bb458845b..1de5982ba 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/VariableNamingWithoutSymbols.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/VariableNamingWithoutSymbols.cs @@ -77,5 +77,13 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Console.WriteLine(*memory2); } } + + private static void ForLoopNamingConflict(int i) + { + for (int j = 0; j < i; j++) + { + Console.WriteLine(i + " of " + j); + } + } } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.Expected.cs b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.Expected.cs index cfa266a46..3eb91b54a 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.Expected.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.Expected.cs @@ -96,25 +96,25 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly thisField = this, field1 = i }; - int field1 = default(int); - string field2 = default(string); + int field5 = default(int); + string field4 = default(string); DisplayClass field3 = default(DisplayClass); while (true) { switch (Rand()) { case 1: - field1 = Rand(); + field5 = Rand(); continue; case 2: - field2 = Rand().ToString(); + field4 = Rand().ToString(); continue; case 3: field3 = displayClass; continue; } - Console.WriteLine(field1); - Console.WriteLine(field2); + Console.WriteLine(field5); + Console.WriteLine(field4); Console.WriteLine(field3); } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs index 4b9dcc5dd..9b832955c 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs @@ -55,7 +55,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms ILTransformContext context; List currentLowerCaseTypeOrMemberNames; Dictionary reservedVariableNames; - Dictionary localFunctionMapping; HashSet loopCounters; const char maxLoopVariableName = 'n'; int numDisplayClassLocals; @@ -75,7 +74,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms currentLowerCaseTypeOrMemberNames.Add(name); AddExistingName(reservedVariableNames, name); } - localFunctionMapping = new Dictionary(); loopCounters = CollectLoopCounters(function); foreach (var f in function.Descendants.OfType()) { @@ -153,10 +151,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } numDisplayClassLocals = 0; - foreach (ILFunction f in function.Descendants.OfType().Reverse()) - { - PerformAssignment(f); - } + PerformAssignment(function); } static IEnumerable CollectAllLowerCaseMemberNames(ITypeDefinition type) @@ -195,128 +190,131 @@ namespace ICSharpCode.Decompiler.IL.Transforms void PerformAssignment(ILFunction function) { - // remove unused variables before assigning names - function.Variables.RemoveDead(); - Dictionary assignedLocalSignatureIndices = new Dictionary(); - foreach (var v in function.Variables.OrderBy(v => v.Name)) + var localFunctionMapping = new Dictionary(); + var variableMapping = new Dictionary(ILVariableEqualityComparer.Instance); + var assignedLocalSignatureIndices = new Dictionary<(ILFunction, int), string>(); + + foreach (var inst in function.Descendants) { - switch (v.Kind) + if (inst is ILFunction { Kind: ILFunctionKind.LocalFunction } localFunction) { - case VariableKind.Parameter: - // Parameter names are handled in ILReader.CreateILVariable - // and CSharpDecompiler.FixParameterNames - break; - case VariableKind.InitializerTarget: // keep generated names - AddExistingName(reservedVariableNames, v.Name); - break; - case VariableKind.DisplayClassLocal: - v.Name = "CS$<>8__locals" + (numDisplayClassLocals++); - break; - case VariableKind.Local when v.Index != null: - if (assignedLocalSignatureIndices.TryGetValue(v.Index.Value, out string name)) + // assign names to local functions + if (!LocalFunctionDecompiler.ParseLocalFunctionName(localFunction.Name, out _, out var newName) || !IsValidName(newName)) + newName = null; + if (newName == null) + { + string nameWithoutNumber = "f"; + if (!reservedVariableNames.TryGetValue(nameWithoutNumber, out int currentIndex)) + { + currentIndex = 1; + } + int count = Math.Max(1, currentIndex) + 1; + reservedVariableNames[nameWithoutNumber] = count; + if (count > 1) { - // make sure all local ILVariables that refer to the same slot in the locals signature - // are assigned the same name. - v.Name = name; + newName = nameWithoutNumber + count.ToString(); } else { - AssignName(); - // Remember the newly assigned name: - assignedLocalSignatureIndices.Add(v.Index.Value, v.Name); + newName = nameWithoutNumber; } - break; - default: - AssignName(); - break; + } + localFunction.Name = newName; + localFunction.ReducedMethod.Name = newName; + localFunctionMapping[(MethodDefinitionHandle)localFunction.ReducedMethod.MetadataToken] = newName; } - - void AssignName() + else if (inst is IInstructionWithVariableOperand i) { - if (v.HasGeneratedName || !IsValidName(v.Name)) + var v = i.Variable; + // if there is already a valid name for the variable slot, just use it + if (variableMapping.TryGetValue(v, out string name)) { - // don't use the name from the debug symbols if it looks like a generated name - v.Name = null; + v.Name = name; + continue; } - else + switch (v.Kind) { - // use the name from the debug symbols and update index appended to duplicates - string nameWithoutNumber = SplitName(v.Name, out int newIndex); - if (!reservedVariableNames.TryGetValue(nameWithoutNumber, out int currentIndex)) - { - currentIndex = 1; - } - reservedVariableNames[nameWithoutNumber] = Math.Max(newIndex, currentIndex); + case VariableKind.Parameter: + // Parameter names are handled in ILReader.CreateILVariable + // and CSharpDecompiler.FixParameterNames + break; + case VariableKind.InitializerTarget: // keep generated names + AddExistingName(reservedVariableNames, v.Name); + break; + case VariableKind.DisplayClassLocal: + v.Name = "CS$<>8__locals" + (numDisplayClassLocals++); + break; + case VariableKind.Local when v.Index != null: + if (assignedLocalSignatureIndices.TryGetValue((v.Function, v.Index.Value), out name)) + { + // make sure all local ILVariables that refer to the same slot in the locals signature + // are assigned the same name. + v.Name = name; + } + else + { + v.Name = AssignName(v, variableMapping); + // Remember the newly assigned name: + assignedLocalSignatureIndices.Add((v.Function, v.Index.Value), v.Name); + } + break; + default: + v.Name = AssignName(v, variableMapping); + break; + } + } + else if (inst is (Call or LdFtn) and IInstructionWithMethodOperand m) + { + // update references to local functions + if (m.Method is LocalFunctionMethod lf + && localFunctionMapping.TryGetValue((MethodDefinitionHandle)lf.MetadataToken, out var name)) + { + lf.Name = name; } } } - foreach (var localFunction in function.LocalFunctions) + } + + string AssignName(ILVariable v, Dictionary variableMapping) + { + // variable has no valid name + string newName = v.Name; + if (v.HasGeneratedName || !IsValidName(newName)) { - if (!LocalFunctionDecompiler.ParseLocalFunctionName(localFunction.Name, out _, out var newName) || !IsValidName(newName)) - newName = null; - localFunction.Name = newName; - localFunction.ReducedMethod.Name = newName; + // don't use the name from the debug symbols if it looks like a generated name + // generate a new one based on how the variable is used + newName = GenerateNameForVariable(v); } - // Now generate names: - var mapping = new Dictionary(ILVariableEqualityComparer.Instance); - foreach (var inst in function.Descendants.OfType()) + // use the existing name and update index appended to future conflicts + string nameWithoutNumber = SplitName(newName, out int newIndex); + if (reservedVariableNames.TryGetValue(nameWithoutNumber, out int lastUsedIndex)) { - var v = inst.Variable; - if (!mapping.TryGetValue(v, out string name)) + // name without number was already used + if (v.Type.IsKnownType(KnownTypeCode.Int32) && loopCounters.Contains(v)) { - if (string.IsNullOrEmpty(v.Name)) - v.Name = GenerateNameForVariable(v); - mapping.Add(v, v.Name); + // special case for loop counters, + // we don't want them to be named i, i2, ..., but i, j, ... + newName = GenerateNameForVariable(v); } else { - v.Name = name; - } - } - foreach (var localFunction in function.LocalFunctions) - { - var newName = localFunction.Name; - if (newName == null) - { - string nameWithoutNumber = "f"; - if (!reservedVariableNames.TryGetValue(nameWithoutNumber, out int currentIndex)) + if (newIndex > lastUsedIndex) { - currentIndex = 1; - } - int count = Math.Max(1, currentIndex) + 1; - reservedVariableNames[nameWithoutNumber] = count; - if (count > 1) - { - newName = nameWithoutNumber + count.ToString(); + // new index is larger than last, so we can use it } else { - newName = nameWithoutNumber; + // new index is smaller or equal, so we use the next value + newIndex = lastUsedIndex + 1; } + // resolve conflicts by appending the index to the new name: + newName = nameWithoutNumber + newIndex.ToString(); } - localFunction.Name = newName; - localFunction.ReducedMethod.Name = newName; - localFunctionMapping[(MethodDefinitionHandle)localFunction.ReducedMethod.MetadataToken] = newName; - } - foreach (var inst in function.Descendants) - { - LocalFunctionMethod localFunction; - switch (inst) - { - case Call call: - localFunction = call.Method as LocalFunctionMethod; - break; - case LdFtn ldftn: - localFunction = ldftn.Method as LocalFunctionMethod; - break; - default: - localFunction = null; - break; - } - if (localFunction == null || !localFunctionMapping.TryGetValue((MethodDefinitionHandle)localFunction.MetadataToken, out var name)) - continue; - localFunction.Name = name; } + // update the last used index + reservedVariableNames[nameWithoutNumber] = newIndex; + variableMapping.Add(v, newName); + return newName; } /// @@ -459,23 +457,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms proposedName = GetNameByType(variable.Type); } - // remove any numbers from the proposed name - proposedName = SplitName(proposedName, out int number); - - if (!reservedVariableNames.ContainsKey(proposedName)) - { - reservedVariableNames.Add(proposedName, 0); - } - int count = ++reservedVariableNames[proposedName]; - Debug.Assert(!string.IsNullOrWhiteSpace(proposedName)); - if (count > 1) - { - return proposedName + count.ToString(); - } - else - { - return proposedName; - } + // for generated names remove number-suffixes + return SplitName(proposedName, out _); } static string GetNameFromInstruction(ILInstruction inst) diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index c3b464399..ed086d68f 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -898,7 +898,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms } context.Step("TransformCatchVariable", entryPoint.Instructions[0]); exceptionVar.Kind = VariableKind.ExceptionLocal; + exceptionVar.Name = handler.Variable.Name; exceptionVar.Type = handler.Variable.Type; + exceptionVar.HasGeneratedName = handler.Variable.HasGeneratedName; handler.Variable = exceptionVar; if (isCatchBlock) { From 45cfb8fdbeea65a696a6f579809e7836a78d112a Mon Sep 17 00:00:00 2001 From: tom-englert Date: Sat, 3 Aug 2024 15:10:02 +0200 Subject: [PATCH 04/35] Fix #3249: Settings panel shows strange hover/selection behavior --- ILSpy/Options/DecompilerSettingsPanel.xaml | 82 ++++++++++------------ ILSpy/Options/DisplaySettingsPanel.xaml | 1 - 2 files changed, 39 insertions(+), 44 deletions(-) diff --git a/ILSpy/Options/DecompilerSettingsPanel.xaml b/ILSpy/Options/DecompilerSettingsPanel.xaml index 2e16f5054..f257d8654 100644 --- a/ILSpy/Options/DecompilerSettingsPanel.xaml +++ b/ILSpy/Options/DecompilerSettingsPanel.xaml @@ -1,9 +1,12 @@  + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:properties="clr-namespace:ICSharpCode.ILSpy.Properties" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:options="clr-namespace:ICSharpCode.ILSpy.Options" + d:DataContext="{d:DesignInstance options:DecompilerSettingsViewModel}"> @@ -20,44 +23,37 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ILSpy/Options/DisplaySettingsPanel.xaml b/ILSpy/Options/DisplaySettingsPanel.xaml index 846a33c06..52f5c3ab1 100644 --- a/ILSpy/Options/DisplaySettingsPanel.xaml +++ b/ILSpy/Options/DisplaySettingsPanel.xaml @@ -7,7 +7,6 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:themes="clr-namespace:ICSharpCode.ILSpy.Themes" - xmlns:toms="urn:TomsToolbox" d:DataContext="{d:DesignInstance local:DisplaySettingsViewModel}"> From d435f5ffafac6279ebe259940c60fae027ccb4d5 Mon Sep 17 00:00:00 2001 From: tom-englert Date: Sat, 3 Aug 2024 15:55:20 +0200 Subject: [PATCH 05/35] Fix #3251: Decompiler Settings: CheckBox in group header does not reflect state of the group --- ILSpy/Options/DecompilerSettingsPanel.xaml | 50 ++++----- ILSpy/Options/DecompilerSettingsPanel.xaml.cs | 59 +--------- ILSpy/Options/DecompilerSettingsViewModel.cs | 101 ++++++++++++------ 3 files changed, 89 insertions(+), 121 deletions(-) diff --git a/ILSpy/Options/DecompilerSettingsPanel.xaml b/ILSpy/Options/DecompilerSettingsPanel.xaml index f257d8654..2078217e4 100644 --- a/ILSpy/Options/DecompilerSettingsPanel.xaml +++ b/ILSpy/Options/DecompilerSettingsPanel.xaml @@ -7,13 +7,6 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:options="clr-namespace:ICSharpCode.ILSpy.Options" d:DataContext="{d:DesignInstance options:DecompilerSettingsViewModel}"> - - - - - - - @@ -24,33 +17,26 @@ - - - - - - - - + - + + + + + + + + + + + + + + diff --git a/ILSpy/Options/DecompilerSettingsPanel.xaml.cs b/ILSpy/Options/DecompilerSettingsPanel.xaml.cs index aec4ced54..9f542f243 100644 --- a/ILSpy/Options/DecompilerSettingsPanel.xaml.cs +++ b/ILSpy/Options/DecompilerSettingsPanel.xaml.cs @@ -16,12 +16,6 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -using System.ComponentModel; -using System.Linq; -using System.Reflection; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; using System.Xml.Linq; using ICSharpCode.ILSpyX.Settings; @@ -32,7 +26,7 @@ namespace ICSharpCode.ILSpy.Options /// Interaction logic for DecompilerSettingsPanel.xaml /// [ExportOptionPage(Title = nameof(Properties.Resources.Decompiler), Order = 10)] - internal partial class DecompilerSettingsPanel : UserControl, IOptionPage + internal partial class DecompilerSettingsPanel : IOptionPage { public DecompilerSettingsPanel() { @@ -59,58 +53,9 @@ namespace ICSharpCode.ILSpy.Options MainWindow.Instance.AssemblyListManager.UseDebugSymbols = newSettings.UseDebugSymbols; } - private void OnGroupChecked(object sender, RoutedEventArgs e) - { - CheckGroup((CollectionViewGroup)((CheckBox)sender).DataContext, true); - } - private void OnGroupUnchecked(object sender, RoutedEventArgs e) - { - CheckGroup((CollectionViewGroup)((CheckBox)sender).DataContext, false); - } - - void CheckGroup(CollectionViewGroup group, bool value) - { - foreach (var item in group.Items) - { - switch (item) - { - case CollectionViewGroup subGroup: - CheckGroup(subGroup, value); - break; - case CSharpDecompilerSetting setting: - setting.IsEnabled = value; - break; - } - } - } - - bool IsGroupChecked(CollectionViewGroup group) - { - bool value = true; - foreach (var item in group.Items) - { - switch (item) - { - case CollectionViewGroup subGroup: - value = value && IsGroupChecked(subGroup); - break; - case CSharpDecompilerSetting setting: - value = value && setting.IsEnabled; - break; - } - } - return value; - } - - private void OnGroupLoaded(object sender, RoutedEventArgs e) - { - CheckBox checkBox = (CheckBox)sender; - checkBox.IsChecked = IsGroupChecked((CollectionViewGroup)checkBox.DataContext); - } - public void LoadDefaults() { - MainWindow.Instance.CurrentDecompilerSettings = new Decompiler.DecompilerSettings(); + MainWindow.Instance.CurrentDecompilerSettings = new(); this.DataContext = new DecompilerSettingsViewModel(MainWindow.Instance.CurrentDecompilerSettings); } } diff --git a/ILSpy/Options/DecompilerSettingsViewModel.cs b/ILSpy/Options/DecompilerSettingsViewModel.cs index b7ce36fbe..3a574cca9 100644 --- a/ILSpy/Options/DecompilerSettingsViewModel.cs +++ b/ILSpy/Options/DecompilerSettingsViewModel.cs @@ -16,83 +16,120 @@ // 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.ComponentModel; using System.Linq; using System.Reflection; -using System.Runtime.CompilerServices; using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.TreeNodes; +using TomsToolbox.Wpf; + namespace ICSharpCode.ILSpy.Options { - public class DecompilerSettingsViewModel : INotifyPropertyChanged + public sealed class DecompilerSettingsViewModel : ObservableObjectBase { - public CSharpDecompilerSetting[] Settings { get; set; } + public DecompilerSettingsGroupViewModel[] Settings { get; } public DecompilerSettingsViewModel(Decompiler.DecompilerSettings settings) { Settings = typeof(Decompiler.DecompilerSettings).GetProperties() .Where(p => p.GetCustomAttribute()?.Browsable != false) - .Select(p => new CSharpDecompilerSetting(p) { IsEnabled = (bool)p.GetValue(settings) }) + .Select(p => new DecompilerSettingsItemViewModel(p) { IsEnabled = p.GetValue(settings) is true }) .OrderBy(item => item.Category, NaturalStringComparer.Instance) - .ThenBy(item => item.Description) + .GroupBy(p => p.Category) + .Select(g => new DecompilerSettingsGroupViewModel(g.Key, g.OrderBy(i => i.Description).ToArray())) .ToArray(); } - public event PropertyChangedEventHandler PropertyChanged; - - protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - public Decompiler.DecompilerSettings ToDecompilerSettings() { var settings = new Decompiler.DecompilerSettings(); - foreach (var item in Settings) + + foreach (var item in Settings.SelectMany(group => group.Settings)) { item.Property.SetValue(settings, item.IsEnabled); } + return settings; } } - public class CSharpDecompilerSetting : INotifyPropertyChanged + + public sealed class DecompilerSettingsGroupViewModel : ObservableObjectBase { - bool isEnabled; + private bool? _areAllItemsChecked; - public CSharpDecompilerSetting(PropertyInfo p) + public DecompilerSettingsGroupViewModel(string category, DecompilerSettingsItemViewModel[] settings) { - this.Property = p; - this.Category = GetResourceString(p.GetCustomAttribute()?.Category ?? Resources.Other); - this.Description = GetResourceString(p.GetCustomAttribute()?.Description ?? p.Name); - } + Settings = settings; + Category = category; - public PropertyInfo Property { get; } + _areAllItemsChecked = GetAreAllItemsChecked(Settings); - public bool IsEnabled { - get => isEnabled; + foreach (DecompilerSettingsItemViewModel viewModel in settings) + { + viewModel.PropertyChanged += Item_PropertyChanged; + } + } + + public bool? AreAllItemsChecked { + get => _areAllItemsChecked; set { - if (value != isEnabled) + SetProperty(ref _areAllItemsChecked, value); + + if (!value.HasValue) + return; + + foreach (var setting in Settings) { - isEnabled = value; - OnPropertyChanged(); + setting.IsEnabled = value.Value; } } } - public string Description { get; set; } + public string Category { get; } - public string Category { get; set; } + public DecompilerSettingsItemViewModel[] Settings { get; } - public event PropertyChangedEventHandler PropertyChanged; + private void Item_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(DecompilerSettingsItemViewModel.IsEnabled)) + { + AreAllItemsChecked = GetAreAllItemsChecked(Settings); + } + } - protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + private static bool? GetAreAllItemsChecked(ICollection settings) { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + var numberOfEnabledItems = settings.Count(item => item.IsEnabled); + + if (numberOfEnabledItems == settings.Count) + return true; + + if (numberOfEnabledItems == 0) + return false; + + return null; + } + } + + public sealed class DecompilerSettingsItemViewModel(PropertyInfo property) : ObservableObjectBase + { + private bool _isEnabled; + + public PropertyInfo Property { get; } = property; + + public bool IsEnabled { + get => _isEnabled; + set => SetProperty(ref _isEnabled, value); } - static string GetResourceString(string key) + public string Description { get; set; } = GetResourceString(property.GetCustomAttribute()?.Description ?? property.Name); + + public string Category { get; set; } = GetResourceString(property.GetCustomAttribute()?.Category ?? Resources.Other); + + private static string GetResourceString(string key) { var str = !string.IsNullOrEmpty(key) ? Resources.ResourceManager.GetString(key) : null; return string.IsNullOrEmpty(key) || string.IsNullOrEmpty(str) ? key : str; From 1520b4134193742813fc21d7bb03dcfdb449365d Mon Sep 17 00:00:00 2001 From: tom-englert Date: Sat, 3 Aug 2024 16:05:46 +0200 Subject: [PATCH 06/35] Simplify layout --- ILSpy/Options/DecompilerSettingsPanel.xaml | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/ILSpy/Options/DecompilerSettingsPanel.xaml b/ILSpy/Options/DecompilerSettingsPanel.xaml index 2078217e4..fe4368243 100644 --- a/ILSpy/Options/DecompilerSettingsPanel.xaml +++ b/ILSpy/Options/DecompilerSettingsPanel.xaml @@ -7,16 +7,9 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:options="clr-namespace:ICSharpCode.ILSpy.Options" d:DataContext="{d:DesignInstance options:DecompilerSettingsViewModel}"> - - - - - - - - - - + + + @@ -41,5 +34,5 @@ - + \ No newline at end of file From 13fd9030165f4d090ed0dd905d86b65df60c3c0a Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Tue, 9 Jul 2024 18:41:50 +0200 Subject: [PATCH 07/35] Add extension method to get metadata TypeAttributes for an ITypeDefinition --- ICSharpCode.Decompiler/NRExtensions.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/NRExtensions.cs b/ICSharpCode.Decompiler/NRExtensions.cs index 2d978a777..58850d2b8 100644 --- a/ICSharpCode.Decompiler/NRExtensions.cs +++ b/ICSharpCode.Decompiler/NRExtensions.cs @@ -16,7 +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.Collections.Generic; +using System.Reflection.Metadata; using ICSharpCode.Decompiler.Documentation; using ICSharpCode.Decompiler.TypeSystem; @@ -99,5 +99,13 @@ namespace ICSharpCode.Decompiler return null; return docProvider.GetDocumentation(entity); } + + internal static System.Reflection.TypeAttributes GetMetadataAttributes(this ITypeDefinition type) + { + var metadata = type.ParentModule.MetadataFile?.Metadata; + if (metadata == null || type.MetadataToken.IsNil) + return 0; + return metadata.GetTypeDefinition((TypeDefinitionHandle)type.MetadataToken).Attributes; + } } } From e9949df54142225112df3d82a2761c0dd06a5d9a Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Tue, 9 Jul 2024 19:01:08 +0200 Subject: [PATCH 08/35] Add missing property and event definitions when a single constructor was selected in the ILSpy tree view. --- ILSpy/Languages/CSharpLanguage.cs | 36 +++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/ILSpy/Languages/CSharpLanguage.cs b/ILSpy/Languages/CSharpLanguage.cs index 212d4f870..6e8296241 100644 --- a/ILSpy/Languages/CSharpLanguage.cs +++ b/ILSpy/Languages/CSharpLanguage.cs @@ -205,19 +205,33 @@ namespace ICSharpCode.ILSpy removedSymbols.Add(fd.GetSymbol()); } break; + case EventDeclaration ed: + // Remove any events without initializers + if (ed.Variables.All(v => v.Initializer.IsNull)) + { + ed.Remove(); + removedSymbols.Add(ed.GetSymbol()); + } + break; + case PropertyDeclaration pd: + // Remove any properties without initializers + if (pd.Initializer.IsNull) + { + pd.Remove(); + removedSymbols.Add(pd.GetSymbol()); + } + break; } } if (ctorDecl?.Initializer.ConstructorInitializerType == ConstructorInitializerType.This) { - // remove all fields + // remove all non-constructor declarations foreach (var node in rootNode.Children) { - switch (node) + if (node is not ConstructorDeclaration) { - case FieldDeclaration fd: - fd.Remove(); - removedSymbols.Add(fd.GetSymbol()); - break; + node.Remove(); + removedSymbols.Add(node.GetSymbol()); } } } @@ -269,6 +283,16 @@ namespace ICSharpCode.ILSpy if (!field.MetadataToken.IsNil && field.IsStatic == isStatic) members.Add(field.MetadataToken); } + foreach (var e in type.Events) + { + if (!e.MetadataToken.IsNil && e.IsStatic == isStatic) + members.Add(e.MetadataToken); + } + foreach (var p in type.Properties) + { + if (!p.MetadataToken.IsNil && p.IsStatic == isStatic) + members.Add(p.MetadataToken); + } foreach (var ctor in type.Methods) { if (!ctor.MetadataToken.IsNil && ctor.IsConstructor && ctor.IsStatic == isStatic) From 2043e5dd6fad27340a58fa18ee6488a349fb500b Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 3 Aug 2024 20:25:07 +0200 Subject: [PATCH 09/35] Add support for C# 12 primary constructors. --- .../TestCases/ILPretty/FSharpLoops_Debug.cs | 13 +--- .../TestCases/ILPretty/FSharpLoops_Release.cs | 14 +--- .../Pretty/ConstructorInitializers.cs | 53 ++++++++++++++ .../CSharp/CSharpDecompiler.cs | 63 +++++++++++------ .../CSharp/RecordDecompiler.cs | 70 +++++++++++++++---- ...ransformFieldAndConstructorInitializers.cs | 53 +++++++++++--- ICSharpCode.Decompiler/DecompilerSettings.cs | 21 +++++- ILSpy/Properties/Resources.Designer.cs | 10 +++ ILSpy/Properties/Resources.resx | 3 + 9 files changed, 235 insertions(+), 65 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpLoops_Debug.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpLoops_Debug.cs index 584b75b18..62762e5ef 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpLoops_Debug.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpLoops_Debug.cs @@ -50,24 +50,17 @@ public static class Program [Serializable] [SpecialName] [CompilationMapping(SourceConstructFlags.Closure)] - internal sealed class getSeq_00405 : GeneratedSequenceBase + internal sealed class getSeq_00405(int pc, int current) : GeneratedSequenceBase() { [DebuggerNonUserCode] [DebuggerBrowsable(DebuggerBrowsableState.Never)] [CompilerGenerated] - public int pc; + public int pc = pc; [DebuggerNonUserCode] [DebuggerBrowsable(DebuggerBrowsableState.Never)] [CompilerGenerated] - public int current; - - public getSeq_00405(int pc, int current) - { - this.pc = pc; - this.current = current; - base._002Ector(); - } + public int current = current; public override int GenerateNext(ref IEnumerable next) { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpLoops_Release.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpLoops_Release.cs index 7d6c0e14b..e8513b055 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpLoops_Release.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpLoops_Release.cs @@ -1,4 +1,3 @@ - // C:\Users\Siegfried\Documents\Visual Studio 2017\Projects\ConsoleApp13\ConsoleApplication1\bin\Release\ConsoleApplication1.exe // ConsoleApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null // Global type: @@ -51,24 +50,17 @@ public static class Program [Serializable] [SpecialName] [CompilationMapping(SourceConstructFlags.Closure)] - internal sealed class getSeq_00405 : GeneratedSequenceBase + internal sealed class getSeq_00405(int pc, int current) : GeneratedSequenceBase() { [DebuggerNonUserCode] [DebuggerBrowsable(DebuggerBrowsableState.Never)] [CompilerGenerated] - public int pc; + public int pc = pc; [DebuggerNonUserCode] [DebuggerBrowsable(DebuggerBrowsableState.Never)] [CompilerGenerated] - public int current; - - public getSeq_00405(int pc, int current) - { - this.pc = pc; - this.current = current; - base._002Ector(); - } + public int current = current; public override int GenerateNext(ref IEnumerable next) { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ConstructorInitializers.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ConstructorInitializers.cs index 7b60f3ad4..5b08837a0 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ConstructorInitializers.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ConstructorInitializers.cs @@ -80,5 +80,58 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public unsafe static int StaticSizeOf = sizeof(SimpleStruct); public unsafe int SizeOf = sizeof(SimpleStruct); } + + +#if CS120 + public class ClassWithPrimaryCtorUsingGlobalParameter(int a) + { + public void Print() + { + Console.WriteLine(a); + } + } + + public class ClassWithPrimaryCtorUsingGlobalParameterAssignedToField(int a) + { + private readonly int a = a; + + public void Print() + { + Console.WriteLine(a); + } + } + + public class ClassWithPrimaryCtorUsingGlobalParameterAssignedToFieldAndUsedInMethod(int a) + { +#pragma warning disable CS9124 // Parameter is captured into the state of the enclosing type and its value is also used to initialize a field, property, or event. + private readonly int _a = a; +#pragma warning restore CS9124 // Parameter is captured into the state of the enclosing type and its value is also used to initialize a field, property, or event. + + public void Print() + { + Console.WriteLine(a); + } + } + + public class ClassWithPrimaryCtorUsingGlobalParameterAssignedToProperty(int a) + { + public int A { get; set; } = a; + + public void Print() + { + Console.WriteLine(A); + } + } + + public class ClassWithPrimaryCtorUsingGlobalParameterAssignedToEvent(EventHandler a) + { + public event EventHandler A = a; + + public void Print() + { + Console.WriteLine(this.A); + } + } +#endif } } diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index b6a56b1ab..520619787 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -337,6 +337,8 @@ namespace ICSharpCode.Decompiler.CSharp { if (settings.AnonymousMethods && IsAnonymousMethodCacheField(field, metadata)) return true; + if (settings.UsePrimaryConstructorSyntaxForNonRecordTypes && IsPrimaryConstructorParameterBackingField(field, metadata)) + return true; if (settings.AutomaticProperties && IsAutomaticPropertyBackingField(field, metadata, out var propertyName)) { if (!settings.GetterOnlyAutomaticProperties && IsGetterOnlyProperty(propertyName)) @@ -390,6 +392,11 @@ namespace ICSharpCode.Decompiler.CSharp return false; } + static bool IsPrimaryConstructorParameterBackingField(SRM.FieldDefinition field, MetadataReader metadata) + { + var name = metadata.GetString(field.Name); + return name.StartsWith("<", StringComparison.Ordinal) && name.EndsWith(">P", StringComparison.Ordinal); + } static bool IsSwitchOnStringCache(SRM.FieldDefinition field, MetadataReader metadata) { @@ -1303,12 +1310,12 @@ namespace ICSharpCode.Decompiler.CSharp // e.g. DelegateDeclaration return entityDecl; } - bool isRecord = typeDef.Kind switch { - TypeKind.Class => settings.RecordClasses && typeDef.IsRecord, - TypeKind.Struct => settings.RecordStructs && typeDef.IsRecord, + bool isRecordLike = typeDef.Kind switch { + TypeKind.Class => (settings.RecordClasses && typeDef.IsRecord) || settings.UsePrimaryConstructorSyntaxForNonRecordTypes, + TypeKind.Struct => (settings.RecordStructs && typeDef.IsRecord) || settings.UsePrimaryConstructorSyntaxForNonRecordTypes, _ => false, }; - RecordDecompiler recordDecompiler = isRecord ? new RecordDecompiler(typeSystem, typeDef, settings, CancellationToken) : null; + RecordDecompiler recordDecompiler = isRecordLike ? new RecordDecompiler(typeSystem, typeDef, settings, CancellationToken) : null; if (recordDecompiler != null) decompileRun.RecordDecompilers.Add(typeDef, recordDecompiler); @@ -1318,33 +1325,41 @@ namespace ICSharpCode.Decompiler.CSharp { ParameterDeclaration pd = typeSystemAstBuilder.ConvertParameter(p); (IProperty prop, IField field) = recordDecompiler.GetPropertyInfoByPrimaryConstructorParameter(p); - Syntax.Attribute[] attributes = prop.GetAttributes().Select(attr => typeSystemAstBuilder.ConvertAttribute(attr)).ToArray(); - if (attributes.Length > 0) + + if (prop != null) { - var section = new AttributeSection { - AttributeTarget = "property" - }; - section.Attributes.AddRange(attributes); - pd.Attributes.Add(section); + var attributes = prop?.GetAttributes().Select(attr => typeSystemAstBuilder.ConvertAttribute(attr)).ToArray(); + if (attributes?.Length > 0) + { + var section = new AttributeSection { + AttributeTarget = "property" + }; + section.Attributes.AddRange(attributes); + pd.Attributes.Add(section); + } } - attributes = field.GetAttributes() - .Where(a => !PatternStatementTransform.attributeTypesToRemoveFromAutoProperties.Contains(a.AttributeType.FullName)) - .Select(attr => typeSystemAstBuilder.ConvertAttribute(attr)).ToArray(); - if (attributes.Length > 0) + if (field != null && (recordDecompiler.FieldIsGenerated(field) || typeDef.IsRecord)) { - var section = new AttributeSection { - AttributeTarget = "field" - }; - section.Attributes.AddRange(attributes); - pd.Attributes.Add(section); + var attributes = field.GetAttributes() + .Where(a => !PatternStatementTransform.attributeTypesToRemoveFromAutoProperties.Contains(a.AttributeType.FullName)) + .Select(attr => typeSystemAstBuilder.ConvertAttribute(attr)).ToArray(); + if (attributes.Length > 0) + { + var section = new AttributeSection { + AttributeTarget = "field" + }; + section.Attributes.AddRange(attributes); + pd.Attributes.Add(section); + } } typeDecl.PrimaryConstructorParameters.Add(pd); } } // With C# 9 records, the relative order of fields and properties matters: - IEnumerable fieldsAndProperties = recordDecompiler?.FieldsAndProperties - ?? typeDef.Fields.Concat(typeDef.Properties); + IEnumerable fieldsAndProperties = isRecordLike && typeDef.IsRecord + ? recordDecompiler.FieldsAndProperties + : typeDef.Fields.Concat(typeDef.Properties); // For COM interop scenarios, the relative order of virtual functions/properties matters: IEnumerable allOrderedMembers = RequiresNativeOrdering(typeDef) ? GetMembersWithNativeOrdering(typeDef) : @@ -1481,6 +1496,10 @@ namespace ICSharpCode.Decompiler.CSharp { return; } + if (recordDecompiler?.FieldIsGenerated(field) == true) + { + return; + } entityDecl = DoDecompile(field, decompileRun, decompilationContext.WithCurrentMember(field)); entityMap.Add(field, entityDecl); break; diff --git a/ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs b/ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs index 15d6a6d34..1ddc179b3 100644 --- a/ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs @@ -43,8 +43,8 @@ namespace ICSharpCode.Decompiler.CSharp readonly IType baseClass; readonly Dictionary backingFieldToAutoProperty = new Dictionary(); readonly Dictionary autoPropertyToBackingField = new Dictionary(); - readonly Dictionary primaryCtorParameterToAutoProperty = new Dictionary(); - readonly Dictionary autoPropertyToPrimaryCtorParameter = new Dictionary(); + readonly Dictionary primaryCtorParameterToAutoPropertyOrBackingField = new Dictionary(); + readonly Dictionary autoPropertyOrBackingFieldToPrimaryCtorParameter = new Dictionary(); public RecordDecompiler(IDecompilerTypeSystem dts, ITypeDefinition recordTypeDef, DecompilerSettings settings, CancellationToken cancellationToken) { @@ -78,6 +78,8 @@ namespace ICSharpCode.Decompiler.CSharp bool IsAutoProperty(IProperty p, out IField field) { field = null; + if (p.IsStatic) + return false; if (p.Parameters.Count != 0) return false; if (p.Getter != null) @@ -158,8 +160,18 @@ namespace ICSharpCode.Decompiler.CSharp IMethod DetectPrimaryConstructor() { - if (!settings.UsePrimaryConstructorSyntax) - return null; + if (recordTypeDef.IsRecord) + { + if (!settings.UsePrimaryConstructorSyntax) + return null; + } + else + { + if (!settings.UsePrimaryConstructorSyntaxForNonRecordTypes) + return null; + if (isStruct) + return null; + } var subst = recordTypeDef.AsParameterizedType().GetSubstitution(); foreach (var method in recordTypeDef.Methods) @@ -170,8 +182,8 @@ namespace ICSharpCode.Decompiler.CSharp var m = method.Specialize(subst); if (IsPrimaryConstructor(m, method)) return method; - primaryCtorParameterToAutoProperty.Clear(); - autoPropertyToPrimaryCtorParameter.Clear(); + primaryCtorParameterToAutoPropertyOrBackingField.Clear(); + autoPropertyOrBackingFieldToPrimaryCtorParameter.Clear(); } return null; @@ -205,10 +217,18 @@ namespace ICSharpCode.Decompiler.CSharp return false; if (!(value.Kind == VariableKind.Parameter && value.Index == i)) return false; - if (!backingFieldToAutoProperty.TryGetValue(field, out var property)) - return false; - primaryCtorParameterToAutoProperty.Add(unspecializedMethod.Parameters[i], property); - autoPropertyToPrimaryCtorParameter.Add(property, unspecializedMethod.Parameters[i]); + IMember backingMember; + if (backingFieldToAutoProperty.TryGetValue(field, out var property)) + { + backingMember = property; + } + else + { + backingMember = field; + } + + primaryCtorParameterToAutoPropertyOrBackingField.Add(unspecializedMethod.Parameters[i], backingMember); + autoPropertyOrBackingFieldToPrimaryCtorParameter.Add(backingMember, unspecializedMethod.Parameters[i]); } if (!isStruct) @@ -261,6 +281,9 @@ namespace ICSharpCode.Decompiler.CSharp /// public bool MethodIsGenerated(IMethod method) { + if (!recordTypeDef.IsRecord) + return false; + if (IsCopyConstructor(method)) { return IsGeneratedCopyConstructor(method); @@ -320,6 +343,9 @@ namespace ICSharpCode.Decompiler.CSharp internal bool PropertyIsGenerated(IProperty property) { + if (!recordTypeDef.IsRecord) + return false; + switch (property.Name) { case "EqualityContract" when !isStruct: @@ -329,17 +355,35 @@ namespace ICSharpCode.Decompiler.CSharp } } + internal bool FieldIsGenerated(IField field) + { + if (!settings.UsePrimaryConstructorSyntaxForNonRecordTypes) + return false; + + var name = field.Name; + return name.StartsWith("<", StringComparison.Ordinal) + && name.EndsWith(">P", StringComparison.Ordinal) + && field.IsCompilerGenerated(); + } + public bool IsPropertyDeclaredByPrimaryConstructor(IProperty property) { var subst = recordTypeDef.AsParameterizedType().GetSubstitution(); return primaryCtor != null - && autoPropertyToPrimaryCtorParameter.ContainsKey((IProperty)property.Specialize(subst)); + && autoPropertyOrBackingFieldToPrimaryCtorParameter.ContainsKey((IProperty)property.Specialize(subst)); } internal (IProperty prop, IField field) GetPropertyInfoByPrimaryConstructorParameter(IParameter parameter) { - var prop = primaryCtorParameterToAutoProperty[parameter]; - return (prop, autoPropertyToBackingField[prop]); + var member = primaryCtorParameterToAutoPropertyOrBackingField[parameter]; + if (member is IField field) + return (null, field); + return ((IProperty)member, autoPropertyToBackingField[(IProperty)member]); + } + + internal IParameter GetPrimaryConstructorParameterFromBackingField(IField field) + { + return autoPropertyOrBackingFieldToPrimaryCtorParameter[field]; } public bool IsCopyConstructor(IMethod method) diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs b/ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs index 6d00db35a..8bcf924fa 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs @@ -16,7 +16,6 @@ // 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.Linq; using System.Reflection; @@ -24,7 +23,9 @@ using System.Reflection; using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; +using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.Decompiler.Util; using SRM = System.Reflection.Metadata; @@ -37,10 +38,12 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms public class TransformFieldAndConstructorInitializers : DepthFirstAstVisitor, IAstTransform { TransformContext context; + Dictionary fieldToVariableMap; public void Run(AstNode node, TransformContext context) { this.context = context; + this.fieldToVariableMap = new(); try { @@ -56,6 +59,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms finally { this.context = null; + this.fieldToVariableMap = null; } } @@ -131,6 +135,13 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms newBaseType.BaseType = baseType; ci.Arguments.MoveTo(newBaseType.Arguments); } + if (constructorDeclaration.Parent is TypeDeclaration { PrimaryConstructorParameters: var parameters }) + { + foreach (var (cpd, ppd) in constructorDeclaration.Parameters.Zip(parameters)) + { + ppd.CopyAnnotationsFrom(cpd); + } + } constructorDeclaration.Remove(); } } @@ -203,18 +214,19 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms if (fieldOrPropertyOrEventDecl is CustomEventDeclaration) break; - Expression initializer = m.Get("initializer").Single(); // 'this'/'base' cannot be used in initializers if (initializer.DescendantsAndSelf.Any(n => n is ThisReferenceExpression || n is BaseReferenceExpression)) break; - - if (initializer.Annotation()?.Variable.Kind == IL.VariableKind.Parameter) + var v = initializer.Annotation()?.Variable; + if (v?.Kind == IL.VariableKind.Parameter) { // remove record ctor parameter assignments - if (!IsPropertyDeclaredByPrimaryCtor(fieldOrPropertyOrEvent as IProperty, record)) + if (!IsPropertyDeclaredByPrimaryCtor(fieldOrPropertyOrEvent, record)) break; isStructPrimaryCtor = true; + if (fieldOrPropertyOrEvent is IField f) + fieldToVariableMap.Add(f, v); } else { @@ -264,11 +276,21 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms } } - bool IsPropertyDeclaredByPrimaryCtor(IProperty p, RecordDecompiler record) + bool IsPropertyDeclaredByPrimaryCtor(IMember m, RecordDecompiler record) { - if (p == null || record == null) + if (record == null) return false; - return record.IsPropertyDeclaredByPrimaryConstructor(p); + switch (m) + { + case IProperty p: + return record.IsPropertyDeclaredByPrimaryConstructor(p); + case IField f: + return true; + case IEvent e: + return true; + default: + return false; + } } void RemoveSingleEmptyConstructor(IEnumerable members, ITypeDefinition contextTypeDefinition) @@ -443,5 +465,20 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms return false; } } + + public override void VisitIdentifier(Identifier identifier) + { + if (identifier.Parent?.GetSymbol() is not IField field) + { + return; + } + if (!fieldToVariableMap.TryGetValue(field, out var v)) + { + return; + } + identifier.Parent.RemoveAnnotations(); + identifier.Parent.AddAnnotation(new ILVariableResolveResult(v)); + identifier.ReplaceWith(Identifier.Create(v.Name)); + } } } diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs index c9d2e77ab..773128bc8 100644 --- a/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -162,12 +162,13 @@ namespace ICSharpCode.Decompiler if (languageVersion < CSharp.LanguageVersion.CSharp12_0) { refReadOnlyParameters = false; + usePrimaryConstructorSyntaxForNonRecordTypes = false; } } public CSharp.LanguageVersion GetMinimumRequiredVersion() { - if (refReadOnlyParameters) + if (refReadOnlyParameters || usePrimaryConstructorSyntaxForNonRecordTypes) return CSharp.LanguageVersion.CSharp12_0; if (scopedRef || requiredMembers || numericIntPtr || utf8StringLiterals || unsignedRightShift || checkedOperators) return CSharp.LanguageVersion.CSharp11_0; @@ -2015,6 +2016,24 @@ namespace ICSharpCode.Decompiler } } + bool usePrimaryConstructorSyntaxForNonRecordTypes = true; + + /// + /// Use primary constructor syntax with classes and structs. + /// + [Category("C# 12.0 / VS 2022.8")] + [Description("DecompilerSettings.UsePrimaryConstructorSyntaxForNonRecordTypes")] + public bool UsePrimaryConstructorSyntaxForNonRecordTypes { + get { return usePrimaryConstructorSyntaxForNonRecordTypes; } + set { + if (usePrimaryConstructorSyntaxForNonRecordTypes != value) + { + usePrimaryConstructorSyntaxForNonRecordTypes = value; + OnPropertyChanged(); + } + } + } + bool separateLocalVariableDeclarations = false; /// diff --git a/ILSpy/Properties/Resources.Designer.cs b/ILSpy/Properties/Resources.Designer.cs index 54ec14ad5..ca8b55151 100644 --- a/ILSpy/Properties/Resources.Designer.cs +++ b/ILSpy/Properties/Resources.Designer.cs @@ -1478,6 +1478,16 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to Use primary constructor syntax for non-record types. + /// + public static string DecompilerSettings_UsePrimaryConstructorDecompilerSettings_SyntaxForNonRecordTypes { + get { + return ResourceManager.GetString("DecompilerSettings.UsePrimaryConstructorDecompilerSettings.SyntaxForNonRecordType" + + "s", resourceCulture); + } + } + /// /// Looks up a localized string similar to Use primary constructor syntax with records. /// diff --git a/ILSpy/Properties/Resources.resx b/ILSpy/Properties/Resources.resx index 096c9d74b..b853b0641 100644 --- a/ILSpy/Properties/Resources.resx +++ b/ILSpy/Properties/Resources.resx @@ -516,6 +516,9 @@ Are you sure you want to continue? Use pattern-based fixed statement + + Use primary constructor syntax for non-record types + Use primary constructor syntax with records From 6cee0cdc4b18ed608be118b628cf90931d3a8204 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Tue, 13 Aug 2024 11:41:34 +0200 Subject: [PATCH 10/35] Fix #3258: Move GraphVizGraph and friends to ILSpy and remove InternalsVisibleTo. --- .../FlowAnalysis/ControlFlowNode.cs | 37 ------------------ .../ICSharpCode.Decompiler.csproj | 1 - .../IL/ControlFlow/ControlFlowGraph.cs | 9 ++--- .../Properties/AssemblyInfo.cs | 4 -- ILSpy/Commands/ShowCFGContextMenuEntry.cs | 39 ++++++++++++++++++- .../Util/GraphVizGraph.cs | 2 +- 6 files changed, 43 insertions(+), 49 deletions(-) rename {ICSharpCode.Decompiler => ILSpy}/Util/GraphVizGraph.cs (99%) diff --git a/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs b/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs index bb1365de0..713fd360f 100644 --- a/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs +++ b/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs @@ -129,42 +129,5 @@ namespace ICSharpCode.Decompiler.FlowAnalysis } return false; } - -#if DEBUG - internal static GraphVizGraph ExportGraph(IReadOnlyList nodes, Func labelFunc = null) - { - if (labelFunc == null) - { - labelFunc = node => { - var block = node.UserData as IL.Block; - return block != null ? block.Label : node.UserData?.ToString(); - }; - } - GraphVizGraph g = new GraphVizGraph(); - GraphVizNode[] n = new GraphVizNode[nodes.Count]; - for (int i = 0; i < n.Length; i++) - { - n[i] = new GraphVizNode(nodes[i].UserIndex); - n[i].shape = "box"; - n[i].label = labelFunc(nodes[i]); - g.AddNode(n[i]); - } - foreach (var source in nodes) - { - foreach (var target in source.Successors) - { - g.AddEdge(new GraphVizEdge(source.UserIndex, target.UserIndex)); - } - if (source.ImmediateDominator != null) - { - g.AddEdge( - new GraphVizEdge(source.ImmediateDominator.UserIndex, source.UserIndex) { - color = "green" - }); - } - } - return g; - } -#endif } } diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index ba3a308e8..583adedeb 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -475,7 +475,6 @@ - diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowGraph.cs b/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowGraph.cs index a4cecb709..8eea6edd2 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowGraph.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowGraph.cs @@ -1,10 +1,6 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Diagnostics; -using System.Linq; -using System.Text; using System.Threading; -using System.Threading.Tasks; using ICSharpCode.Decompiler.FlowAnalysis; using ICSharpCode.Decompiler.Util; @@ -36,6 +32,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// internal readonly ControlFlowNode[] cfg; + /// + public IReadOnlyList Nodes => cfg; + /// /// Dictionary from Block to ControlFlowNode. /// Unlike the cfg array, this can be used to discover control flow nodes even after diff --git a/ICSharpCode.Decompiler/Properties/AssemblyInfo.cs b/ICSharpCode.Decompiler/Properties/AssemblyInfo.cs index 170493460..3f6d671f7 100644 --- a/ICSharpCode.Decompiler/Properties/AssemblyInfo.cs +++ b/ICSharpCode.Decompiler/Properties/AssemblyInfo.cs @@ -19,10 +19,6 @@ using System.Runtime.InteropServices; [assembly: InternalsVisibleTo("ICSharpCode.Decompiler.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001004dcf3979c4e902efa4dd2163a039701ed5822e6f1134d77737296abbb97bf0803083cfb2117b4f5446a217782f5c7c634f9fe1fc60b4c11d62c5b3d33545036706296d31903ddcf750875db38a8ac379512f51620bb948c94d0831125fbc5fe63707cbb93f48c1459c4d1749eb7ac5e681a2f0d6d7c60fa527a3c0b8f92b02bf")] -#if DEBUG -[assembly: InternalsVisibleTo("ILSpy, PublicKey=00240000048000009400000006020000002400005253413100040000010001004dcf3979c4e902efa4dd2163a039701ed5822e6f1134d77737296abbb97bf0803083cfb2117b4f5446a217782f5c7c634f9fe1fc60b4c11d62c5b3d33545036706296d31903ddcf750875db38a8ac379512f51620bb948c94d0831125fbc5fe63707cbb93f48c1459c4d1749eb7ac5e681a2f0d6d7c60fa527a3c0b8f92b02bf")] -#endif - [assembly: SuppressMessage("Microsoft.Usage", "CA2243:AttributeStringLiteralsShouldParseCorrectly", Justification = "AssemblyInformationalVersion does not need to be a parsable version")] diff --git a/ILSpy/Commands/ShowCFGContextMenuEntry.cs b/ILSpy/Commands/ShowCFGContextMenuEntry.cs index 217240e95..a3e2b4e29 100644 --- a/ILSpy/Commands/ShowCFGContextMenuEntry.cs +++ b/ILSpy/Commands/ShowCFGContextMenuEntry.cs @@ -1,9 +1,11 @@ using System; +using System.Collections.Generic; using System.Windows; using ICSharpCode.Decompiler.FlowAnalysis; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.IL.ControlFlow; +using ICSharpCode.ILSpy.Util; namespace ICSharpCode.ILSpy.Commands { @@ -17,7 +19,7 @@ namespace ICSharpCode.ILSpy.Commands { var container = (BlockContainer)context.Reference.Reference; var cfg = new ControlFlowGraph(container); - ControlFlowNode.ExportGraph(cfg.cfg).Show(); + ExportGraph(cfg.Nodes).Show(); } catch (Exception ex) { @@ -34,6 +36,41 @@ namespace ICSharpCode.ILSpy.Commands { return context.Reference?.Reference is BlockContainer; } + + internal static GraphVizGraph ExportGraph(IReadOnlyList nodes, Func labelFunc = null) + { + if (labelFunc == null) + { + labelFunc = node => { + var block = node.UserData as Block; + return block != null ? block.Label : node.UserData?.ToString(); + }; + } + GraphVizGraph g = new GraphVizGraph(); + GraphVizNode[] n = new GraphVizNode[nodes.Count]; + for (int i = 0; i < n.Length; i++) + { + n[i] = new GraphVizNode(nodes[i].UserIndex); + n[i].shape = "box"; + n[i].label = labelFunc(nodes[i]); + g.AddNode(n[i]); + } + foreach (var source in nodes) + { + foreach (var target in source.Successors) + { + g.AddEdge(new GraphVizEdge(source.UserIndex, target.UserIndex)); + } + if (source.ImmediateDominator != null) + { + g.AddEdge( + new GraphVizEdge(source.ImmediateDominator.UserIndex, source.UserIndex) { + color = "green" + }); + } + } + return g; + } } #endif } diff --git a/ICSharpCode.Decompiler/Util/GraphVizGraph.cs b/ILSpy/Util/GraphVizGraph.cs similarity index 99% rename from ICSharpCode.Decompiler/Util/GraphVizGraph.cs rename to ILSpy/Util/GraphVizGraph.cs index 92069e7d9..19ce1cd06 100644 --- a/ICSharpCode.Decompiler/Util/GraphVizGraph.cs +++ b/ILSpy/Util/GraphVizGraph.cs @@ -25,7 +25,7 @@ using System.Globalization; using System.IO; using System.Text.RegularExpressions; -namespace ICSharpCode.Decompiler.Util +namespace ICSharpCode.ILSpy.Util { #if DEBUG /// From cb63f3c873cf5721b12f37292c09eb0eb949b198 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Wed, 14 Aug 2024 07:50:52 +0200 Subject: [PATCH 11/35] Fix #3256: Remove hard-coded Height of PART_searchTextBox in SearchPanel --- ILSpy/TextView/DecompilerTextView.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 00ce4204b..4c53e6c18 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -131,11 +131,14 @@ namespace ICSharpCode.ILSpy.TextView searchPanel.RegisterCommands(Application.Current.MainWindow.CommandBindings); searchPanel.SetResourceReference(SearchPanel.MarkerBrushProperty, ResourceKeys.SearchResultBackgroundBrush); searchPanel.Loaded += (_, _) => { - // HACK: fix the hardcoded but misaligned margin of the search text box. - var textBox = searchPanel.VisualDescendants().OfType().FirstOrDefault(); + // HACK: fix search text box + var textBox = searchPanel.Template.FindName("PART_searchTextBox", searchPanel) as TextBox; if (textBox != null) { + // the hardcoded but misaligned margin textBox.Margin = new Thickness(3); + // the hardcoded height + textBox.Height = double.NaN; } }; From 39d5deb38c15e1ed1397e6009a021114af3dcac4 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 17 Aug 2024 10:09:31 +0200 Subject: [PATCH 12/35] Refresh AssemblyTreeNode tooltip after selecting a new PDB. --- ILSpy/Commands/SelectPdbContextMenuEntry.cs | 4 +++- ILSpy/TreeNodes/AssemblyTreeNode.cs | 10 ++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/ILSpy/Commands/SelectPdbContextMenuEntry.cs b/ILSpy/Commands/SelectPdbContextMenuEntry.cs index 113122953..1d8da975f 100644 --- a/ILSpy/Commands/SelectPdbContextMenuEntry.cs +++ b/ILSpy/Commands/SelectPdbContextMenuEntry.cs @@ -46,7 +46,9 @@ namespace ICSharpCode.ILSpy await assembly.LoadDebugInfo(dlg.FileName); } - MainWindow.Instance.SelectNode(MainWindow.Instance.FindNodeByPath(new[] { assembly.FileName }, true)); + var node = (AssemblyTreeNode)MainWindow.Instance.FindNodeByPath(new[] { assembly.FileName }, true); + node.UpdateToolTip(); + MainWindow.Instance.SelectNode(node); MainWindow.Instance.RefreshDecompiledView(); } diff --git a/ILSpy/TreeNodes/AssemblyTreeNode.cs b/ILSpy/TreeNodes/AssemblyTreeNode.cs index 2449047c7..dc4de23bd 100644 --- a/ILSpy/TreeNodes/AssemblyTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyTreeNode.cs @@ -29,19 +29,19 @@ using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.CSharp.ProjectDecompiler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.ILSpy.Controls.TreeView; using ICSharpCode.ILSpy.Metadata; using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.ViewModels; using ICSharpCode.ILSpyX; using ICSharpCode.ILSpyX.FileLoaders; using ICSharpCode.ILSpyX.PdbProvider; -using ICSharpCode.ILSpy.Controls.TreeView; +using ICSharpCode.ILSpyX.TreeView; using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions; using Microsoft.Win32; using TypeDefinitionHandle = System.Reflection.Metadata.TypeDefinitionHandle; -using ICSharpCode.ILSpyX.TreeView; namespace ICSharpCode.ILSpy.TreeNodes { @@ -168,6 +168,12 @@ namespace ICSharpCode.ILSpy.TreeNodes } } + public void UpdateToolTip() + { + tooltip = null; + RaisePropertyChanged(nameof(ToolTip)); + } + public override bool ShowExpander { get { return !LoadedAssembly.HasLoadError; } } From 930a4a20d1db2d52ccbcd186cc3613d1688a7005 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 17 Aug 2024 10:10:49 +0200 Subject: [PATCH 13/35] Fix #3255: Ignore exceptions while decoding sequence point blobs. --- .../PdbProvider/PortableDebugInfoProvider.cs | 47 +++++++++++-------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/ICSharpCode.ILSpyX/PdbProvider/PortableDebugInfoProvider.cs b/ICSharpCode.ILSpyX/PdbProvider/PortableDebugInfoProvider.cs index 8ebdba9d1..ec4cb8faf 100644 --- a/ICSharpCode.ILSpyX/PdbProvider/PortableDebugInfoProvider.cs +++ b/ICSharpCode.ILSpyX/PdbProvider/PortableDebugInfoProvider.cs @@ -98,31 +98,38 @@ namespace ICSharpCode.ILSpyX.PdbProvider var debugInfo = metadata.GetMethodDebugInformation(method); var sequencePoints = new List(); - foreach (var point in debugInfo.GetSequencePoints()) + try { - string documentFileName; - - if (!point.Document.IsNil) - { - var document = metadata.GetDocument(point.Document); - documentFileName = metadata.GetString(document.Name); - } - else + foreach (var point in debugInfo.GetSequencePoints()) { - documentFileName = ""; + string documentFileName; + + if (!point.Document.IsNil) + { + var document = metadata.GetDocument(point.Document); + documentFileName = metadata.GetString(document.Name); + } + else + { + documentFileName = ""; + } + + sequencePoints.Add(new Decompiler.DebugInfo.SequencePoint() { + Offset = point.Offset, + StartLine = point.StartLine, + StartColumn = point.StartColumn, + EndLine = point.EndLine, + EndColumn = point.EndColumn, + DocumentUrl = documentFileName + }); } - sequencePoints.Add(new Decompiler.DebugInfo.SequencePoint() { - Offset = point.Offset, - StartLine = point.StartLine, - StartColumn = point.StartColumn, - EndLine = point.EndLine, - EndColumn = point.EndColumn, - DocumentUrl = documentFileName - }); + return sequencePoints; + } + catch (BadImageFormatException) + { + return EmptyList.Instance; } - - return sequencePoints; } public IList GetVariables(MethodDefinitionHandle method) From e8a9227483a5184bae388409f8316fa1f9efd20a Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 17 Aug 2024 19:38:53 +0200 Subject: [PATCH 14/35] Fix #3264: Do not allow zero-length segments in CleanUpName: Escape path separator characters at the start of a segment. --- .../CSharp/ProjectDecompiler/WholeProjectDecompiler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs b/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs index ed757600b..f8b687b73 100644 --- a/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs @@ -692,7 +692,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler if (separateAtDots) currentSegmentLength = 0; } - else if (treatAsFileName && (c == '/' || c == '\\') && currentSegmentLength > 0) + else if (treatAsFileName && (c == '/' || c == '\\') && currentSegmentLength > 1) { // if we treat this as a file name, we've started a new segment b.Append(c); From 76cc084dcdea105117a4b84ea829fb7e0c7923f0 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 17 Aug 2024 19:42:03 +0200 Subject: [PATCH 15/35] LazyInitializer.EnsureInitialized does not allow null as return value. Use LazyInit instead. --- ICSharpCode.ILSpyX/LoadedAssembly.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/ICSharpCode.ILSpyX/LoadedAssembly.cs b/ICSharpCode.ILSpyX/LoadedAssembly.cs index 8f7298c84..440ab094b 100644 --- a/ICSharpCode.ILSpyX/LoadedAssembly.cs +++ b/ICSharpCode.ILSpyX/LoadedAssembly.cs @@ -209,14 +209,19 @@ namespace ICSharpCode.ILSpyX /// public ICompilation? GetTypeSystemOrNull() { - return LazyInitializer.EnsureInitialized(ref this.typeSystem, () => { + var value = Volatile.Read(ref this.typeSystem); + if (value == null) + { var module = GetMetadataFileOrNull(); if (module == null || module.IsMetadataOnly) - return null!; - return new SimpleCompilation( + return null; + value = new SimpleCompilation( module.WithOptions(TypeSystemOptions.Default | TypeSystemOptions.Uncached | TypeSystemOptions.KeepModifiers), MinimalCorlib.Instance); - }); + value = LazyInit.GetOrSet(ref this.typeSystem, value); + } + + return value; } readonly object typeSystemWithOptionsLockObj = new object(); From d0d60f9a6dfbe4c4f3f6c7788edf546b36a01da5 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 17 Aug 2024 19:43:18 +0200 Subject: [PATCH 16/35] Fix #3263: Properly handle assemblies that do not provide a type system in DerivedTypesTreeNode. --- ILSpy/TreeNodes/DerivedTypesTreeNode.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/ILSpy/TreeNodes/DerivedTypesTreeNode.cs b/ILSpy/TreeNodes/DerivedTypesTreeNode.cs index 7a0b7accd..d3fd99268 100644 --- a/ILSpy/TreeNodes/DerivedTypesTreeNode.cs +++ b/ILSpy/TreeNodes/DerivedTypesTreeNode.cs @@ -17,7 +17,6 @@ // DEALINGS IN THE SOFTWARE. using System.Collections.Generic; -using System.Linq; using System.Threading; using ICSharpCode.Decompiler; @@ -66,12 +65,16 @@ namespace ICSharpCode.ILSpy.TreeNodes { var definitionMetadata = type.ParentModule.MetadataFile.Metadata; var metadataToken = (SRM.TypeDefinitionHandle)type.MetadataToken; - var assemblies = list.GetAllAssemblies().GetAwaiter().GetResult() - .Select(node => node.GetMetadataFileOrNull()).Where(asm => asm != null).ToArray(); - foreach (var module in assemblies) + var assemblies = list.GetAllAssemblies().GetAwaiter().GetResult(); + foreach (var loadedAssembly in assemblies) { + var module = loadedAssembly.GetMetadataFileOrNull(); + if (module == null) + continue; var metadata = module.Metadata; - var assembly = (MetadataModule)module.GetTypeSystemOrNull().MainModule; + var assembly = module.GetTypeSystemOrNull()?.MainModule as MetadataModule; + if (assembly != null) + continue; foreach (var h in metadata.TypeDefinitions) { cancellationToken.ThrowIfCancellationRequested(); From 63c2bb0b184d1213e28c9fb04155428c7c5bbbfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herrmannsd=C3=B6rfer=2C=20Felix?= Date: Mon, 19 Aug 2024 10:08:08 +0200 Subject: [PATCH 17/35] Ignore empty version directories of dotnet --- .../Metadata/DotNetCorePathFinder.cs | 11 ++++++----- .../Metadata/UniversalAssemblyResolver.cs | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs b/ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs index 67f266c54..a5dd8c200 100644 --- a/ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs +++ b/ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs @@ -249,21 +249,22 @@ namespace ICSharpCode.Decompiler.Metadata static string GetClosestVersionFolder(string basePath, Version version) { var foundVersions = new DirectoryInfo(basePath).GetDirectories() - .Select(d => ConvertToVersion(d.Name)) + .Select(ConvertToVersion) .Where(v => v.version != null); foreach (var folder in foundVersions.OrderBy(v => v.version)) { if (folder.version >= version) - return folder.directoryName; + if(folder.directory.EnumerateFiles().Any()) + return folder.directory.Name; } return version.ToString(); } - internal static (Version version, string directoryName) ConvertToVersion(string name) + internal static (Version version, DirectoryInfo directory) ConvertToVersion(DirectoryInfo directory) { string RemoveTrailingVersionInfo() { - string shortName = name; + string shortName = directory.Name; int dashIndex = shortName.IndexOf('-'); if (dashIndex > 0) { @@ -274,7 +275,7 @@ namespace ICSharpCode.Decompiler.Metadata try { - return (new Version(RemoveTrailingVersionInfo()), name); + return (new Version(RemoveTrailingVersionInfo()), directory); } catch (Exception ex) { diff --git a/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs b/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs index ff27d30d6..90ba8724a 100644 --- a/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs +++ b/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs @@ -386,11 +386,11 @@ namespace ICSharpCode.Decompiler.Metadata string FindClosestVersionDirectory(string basePath, Version? version) { string? path = null; - foreach (var folder in new DirectoryInfo(basePath).GetDirectories().Select(d => DotNetCorePathFinder.ConvertToVersion(d.Name)) + foreach (var folder in new DirectoryInfo(basePath).GetDirectories().Select(DotNetCorePathFinder.ConvertToVersion) .Where(v => v.Item1 != null).OrderByDescending(v => v.Item1)) { if (path == null || version == null || folder.Item1 >= version) - path = folder.Item2; + path = folder.Item2.Name; } return path ?? version?.ToString() ?? "."; } From ea2fc92883e63161f2aa21ac9d3aa121d7ffbec9 Mon Sep 17 00:00:00 2001 From: tom-englert Date: Sun, 11 Aug 2024 17:05:02 +0200 Subject: [PATCH 18/35] Add abstraction layer for MEF --- Directory.Packages.props | 6 +- .../ICSharpCode.Decompiler.ruleset | 4 + ILSpy.sln | 6 ++ ILSpy/App.xaml.cs | 18 +++- ILSpy/ContextMenuEntry.cs | 12 +-- ILSpy/ExportProviderAdapter.cs | 88 +++++++++++++++++++ ILSpy/ILSpy.csproj | 2 + ILSpy/Languages/Languages.cs | 4 +- ILSpy/MainWindow.xaml.cs | 4 +- ILSpy/Options/OptionsDialog.xaml.cs | 10 +-- 10 files changed, 135 insertions(+), 19 deletions(-) create mode 100644 ILSpy/ExportProviderAdapter.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 41b329b4a..1b73cb5e7 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -45,7 +45,9 @@ - - + + + + \ No newline at end of file diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.ruleset b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.ruleset index 66b1288eb..61afdc893 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.ruleset +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.ruleset @@ -79,4 +79,8 @@ + + + + \ No newline at end of file diff --git a/ILSpy.sln b/ILSpy.sln index 5d4e13a93..ce506f4d2 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/App.xaml.cs b/ILSpy/App.xaml.cs index 77722c3ae..fc74c786d 100644 --- a/ILSpy/App.xaml.cs +++ b/ILSpy/App.xaml.cs @@ -42,6 +42,10 @@ using Microsoft.VisualStudio.Composition; 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 @@ namespace ICSharpCode.ILSpy 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,6 +92,12 @@ namespace ICSharpCode.ILSpy } TaskScheduler.UnobservedTaskException += DotNet40_UnobservedTaskException; InitializeMef().GetAwaiter().GetResult(); + + // 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)); + Languages.Initialize(ExportProvider); EventManager.RegisterClassHandler(typeof(Window), Hyperlink.RequestNavigateEvent, @@ -170,8 +179,9 @@ namespace ICSharpCode.ILSpy // 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/ContextMenuEntry.cs b/ILSpy/ContextMenuEntry.cs index 9ce4416a2..fcbdb1bae 100644 --- a/ILSpy/ContextMenuEntry.cs +++ b/ILSpy/ContextMenuEntry.cs @@ -31,6 +31,8 @@ using ICSharpCode.ILSpyX.Search; using ICSharpCode.ILSpy.Controls.TreeView; using ICSharpCode.ILSpyX.TreeView; +using TomsToolbox.Composition; + namespace ICSharpCode.ILSpy { public interface IContextMenuEntry @@ -205,7 +207,7 @@ namespace ICSharpCode.ILSpy readonly DecompilerTextView textView; readonly ListBox listBox; readonly DataGrid dataGrid; - readonly Lazy[] entries; + readonly IExport[] entries; private ContextMenuProvider() { @@ -288,8 +290,8 @@ namespace ICSharpCode.ILSpy 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 @@ namespace ICSharpCode.ILSpy 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/ExportProviderAdapter.cs b/ILSpy/ExportProviderAdapter.cs new file mode 100644 index 000000000..555352fc4 --- /dev/null +++ b/ILSpy/ExportProviderAdapter.cs @@ -0,0 +1,88 @@ +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 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(); + } + +#pragma warning disable CS8769 // Nullability of reference types in type of parameter doesn't match implemented member (possibly because of nullability attributes). + // can't apply NotNullWhen here, because ICSharpCode.Decompiler defines a duplicate attribute, and uses InternalsVisibleTo("ILSpy"), so this attribute is now ambiguous! + bool IExportProvider.TryGetExportedValue(string? contractName, /*[NotNullWhen(true)]*/ out T? value) where T : class +#pragma warning restore CS8769 // Nullability of reference types in type of parameter doesn't match implemented member (possibly because of nullability attributes). + { + 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 7b83970f6..5bbadbe1b 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -50,6 +50,8 @@ + + diff --git a/ILSpy/Languages/Languages.cs b/ILSpy/Languages/Languages.cs index 5721f0e72..cbce690b8 100644 --- a/ILSpy/Languages/Languages.cs +++ b/ILSpy/Languages/Languages.cs @@ -22,6 +22,8 @@ using System.Linq; using Microsoft.VisualStudio.Composition; +using TomsToolbox.Composition; + namespace ICSharpCode.ILSpy { public static class Languages @@ -39,7 +41,7 @@ namespace ICSharpCode.ILSpy get { return allLanguages; } } - internal static void Initialize(ExportProvider ep) + internal static void Initialize(IExportProvider ep) { List languages = new List(); languages.AddRange(ep.GetExportedValues()); diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index e154a67d3..2f8ec46f7 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -62,6 +62,8 @@ using ICSharpCode.ILSpy.Controls.TreeView; using Microsoft.Win32; using ICSharpCode.ILSpyX.TreeView; +using TomsToolbox.Composition; + namespace ICSharpCode.ILSpy { /// @@ -245,7 +247,7 @@ namespace ICSharpCode.ILSpy } - Button MakeToolbarItem(Lazy command) + Button MakeToolbarItem(IExport command) { return new Button { Style = ThemeManager.Current.CreateToolBarButtonStyle(), diff --git a/ILSpy/Options/OptionsDialog.xaml.cs b/ILSpy/Options/OptionsDialog.xaml.cs index e93f08b28..ab15a93d5 100644 --- a/ILSpy/Options/OptionsDialog.xaml.cs +++ b/ILSpy/Options/OptionsDialog.xaml.cs @@ -28,6 +28,8 @@ using System.Xml.Linq; using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpyX.Settings; +using TomsToolbox.Composition; + namespace ICSharpCode.ILSpy.Options { public class TabItemViewModel @@ -47,16 +49,12 @@ namespace ICSharpCode.ILSpy.Options /// public partial class OptionsDialog : Window { - - readonly Lazy[] optionPages; + readonly IExport[] optionPages; public OptionsDialog() { InitializeComponent(); - // These used to have [ImportMany(..., RequiredCreationPolicy = CreationPolicy.NonShared)], so they use their own - // ExportProvider instance. - // FIXME: Ideally, the export provider should be disposed when it's no longer needed. - var ep = App.ExportProviderFactory.CreateExportProvider(); + var ep = App.ExportProvider; this.optionPages = ep.GetExports("OptionPages").ToArray(); ILSpySettings settings = ILSpySettings.Load(); foreach (var optionPage in optionPages.OrderBy(p => p.Metadata.Order)) From 610b0c2ed5583f632573ec669e7bca331f6c8b58 Mon Sep 17 00:00:00 2001 From: tom-englert Date: Mon, 19 Aug 2024 15:06:13 +0200 Subject: [PATCH 19/35] Annotate service lifetime explicitly. --- .editorconfig | 7 ++++++- Directory.Packages.props | 6 ++++-- ICSharpCode.Decompiler/ICSharpCode.Decompiler.ruleset | 1 - .../Analyzers/Builtin/AttributeAppliedToAnalyzer.cs | 2 ++ .../Analyzers/Builtin/EventImplementedByAnalyzer.cs | 2 ++ .../Analyzers/Builtin/EventOverriddenByAnalyzer.cs | 2 ++ .../Analyzers/Builtin/FieldAccessAnalyzer.cs | 3 +++ .../Analyzers/Builtin/MemberImplementsInterfaceAnalyzer.cs | 2 ++ .../Analyzers/Builtin/MethodImplementedByAnalyzer.cs | 2 ++ .../Analyzers/Builtin/MethodOverriddenByAnalyzer.cs | 2 ++ .../Analyzers/Builtin/MethodUsedByAnalyzer.cs | 2 ++ ICSharpCode.ILSpyX/Analyzers/Builtin/MethodUsesAnalyzer.cs | 2 ++ .../Analyzers/Builtin/MethodVirtualUsedByAnalyzer.cs | 2 ++ .../Analyzers/Builtin/PropertyImplementedByAnalyzer.cs | 2 ++ .../Analyzers/Builtin/PropertyOverriddenByAnalyzer.cs | 2 ++ .../Analyzers/Builtin/TypeExposedByAnalyzer.cs | 3 +++ .../Analyzers/Builtin/TypeExtensionMethodsAnalyzer.cs | 2 ++ .../Analyzers/Builtin/TypeInstantiatedByAnalyzer.cs | 2 ++ ICSharpCode.ILSpyX/Analyzers/Builtin/TypeUsedByAnalyzer.cs | 2 ++ ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs | 2 ++ ILSpy.ReadyToRun/ReadyToRunLanguage.cs | 1 + ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml.cs | 2 ++ ILSpy/AboutPage.cs | 2 ++ ILSpy/Analyzers/AnalyzeCommand.cs | 2 ++ ILSpy/Analyzers/CopyAnalysisResultsContextMenuEntry.cs | 2 ++ ILSpy/Analyzers/RemoveAnalyzeContextMenuEntry.cs | 2 ++ ILSpy/Commands/BrowseBackCommand.cs | 2 ++ ILSpy/Commands/BrowseForwardCommand.cs | 2 ++ ILSpy/Commands/CheckForUpdatesCommand.cs | 3 +++ ILSpy/Commands/CopyFullyQualifiedNameContextMenuEntry.cs | 2 ++ ILSpy/Commands/DecompileAllCommand.cs | 3 +++ ILSpy/Commands/DecompileCommand.cs | 2 ++ ILSpy/Commands/DecompileInNewViewCommand.cs | 2 ++ ILSpy/Commands/DisassembleAllCommand.cs | 2 ++ ILSpy/Commands/ExitCommand.cs | 3 +++ ILSpy/Commands/ExtractPackageEntryContextMenuEntry.cs | 2 ++ ILSpy/Commands/GeneratePdbContextMenuEntry.cs | 3 +++ ILSpy/Commands/ManageAssemblyListsCommand.cs | 3 +++ ILSpy/Commands/OpenCommand.cs | 2 ++ ILSpy/Commands/OpenFromGacCommand.cs | 3 +++ ILSpy/Commands/Pdb2XmlCommand.cs | 3 +++ ILSpy/Commands/RefreshCommand.cs | 2 ++ ILSpy/Commands/RemoveAssembliesWithLoadErrors.cs | 3 +++ ILSpy/Commands/SaveCodeContextMenuEntry.cs | 2 ++ ILSpy/Commands/SaveCommand.cs | 2 ++ ILSpy/Commands/ScopeSearchToAssembly.cs | 2 ++ ILSpy/Commands/ScopeSearchToNamespace.cs | 2 ++ ILSpy/Commands/SearchMsdnContextMenuEntry.cs | 3 +++ ILSpy/Commands/SelectPdbContextMenuEntry.cs | 2 ++ ILSpy/Commands/ShowCFGContextMenuEntry.cs | 2 ++ ILSpy/Commands/SortAssemblyListCommand.cs | 3 +++ ILSpy/Docking/CloseAllDocumentsCommand.cs | 3 +++ ILSpy/ExportProviderAdapter.cs | 7 ++----- ILSpy/ILSpy.csproj | 5 ++--- ILSpy/Languages/CSharpILMixedLanguage.cs | 1 + ILSpy/Languages/CSharpLanguage.cs | 1 + ILSpy/Languages/ILLanguage.cs | 1 + ILSpy/Metadata/GoToTokenCommand.cs | 3 +++ ILSpy/Metadata/MetadataProtocolHandler.cs | 1 + ILSpy/Options/DecompilerSettingsPanel.xaml.cs | 2 ++ ILSpy/Options/DisplaySettingsPanel.xaml.cs | 2 ++ ILSpy/Options/MiscSettingsPanel.xaml.cs | 2 ++ ILSpy/Options/OptionsDialog.xaml.cs | 1 + ILSpy/Search/SearchPane.cs | 2 ++ ILSpy/TextView/EditorCommands.cs | 4 ++++ ILSpy/TextView/FoldingCommands.cs | 3 +++ ILSpy/TreeNodes/AssemblyTreeNode.cs | 7 +++++++ ILSpy/TreeNodes/ResourceNodes/CursorResourceEntryNode.cs | 1 + ILSpy/TreeNodes/ResourceNodes/IconResourceEntryNode.cs | 1 + .../TreeNodes/ResourceNodes/ImageListResourceEntryNode.cs | 1 + ILSpy/TreeNodes/ResourceNodes/ImageResourceEntryNode.cs | 1 + ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs | 1 + ILSpy/TreeNodes/ResourceNodes/XamlResourceNode.cs | 1 + ILSpy/TreeNodes/ResourceNodes/XmlResourceNode.cs | 1 + ILSpy/TreeNodes/ThreadingSupport.cs | 2 ++ ILSpy/ViewModels/AnalyzerPaneModel.cs | 4 +++- ILSpy/ViewModels/AssemblyListPaneModel.cs | 4 +++- ILSpy/ViewModels/DebugStepsPaneModel.cs | 4 +++- ILSpy/ViewModels/SearchPaneModel.cs | 4 +++- TestPlugin/AboutPageAddition.cs | 1 + TestPlugin/ContextMenuCommand.cs | 2 ++ TestPlugin/CustomLanguage.cs | 1 + TestPlugin/CustomOptionPage.xaml.cs | 2 ++ TestPlugin/MainMenuCommand.cs | 3 +++ 84 files changed, 184 insertions(+), 16 deletions(-) diff --git a/.editorconfig b/.editorconfig index b7d98cfea..63a46acc9 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,8 @@ 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 + +# Errors and warnings + +# MEF006: No importing constructor +dotnet_diagnostic.MEF006.severity = silent diff --git a/Directory.Packages.props b/Directory.Packages.props index 1b73cb5e7..cc543f59b 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -45,9 +45,11 @@ - - + + + + \ No newline at end of file diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.ruleset b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.ruleset index 61afdc893..053189fe1 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.ruleset +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.ruleset @@ -80,7 +80,6 @@ - \ No newline at end of file diff --git a/ICSharpCode.ILSpyX/Analyzers/Builtin/AttributeAppliedToAnalyzer.cs b/ICSharpCode.ILSpyX/Analyzers/Builtin/AttributeAppliedToAnalyzer.cs index fa925c9b4..f19b23e41 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 @@ using ICSharpCode.Decompiler.Util; 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 e928f531a..e01513d2a 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 5db8f0841..55206ce81 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 ba829b0fc..ad02b999f 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 @@ namespace ICSharpCode.ILSpyX.Analyzers.Builtin /// 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 a25d48d86..4090650d3 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 89da791ed..644212fab 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 ab9c4fe71..c2156d846 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 702d77a7d..a2cb79828 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 2b361ec00..6c7576f7c 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 a24787a88..58d70f3da 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 4188822f4..159d0cf5f 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 1eb0d4a00..da0f9f8ac 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 3f4fd59b2..bfb240bf1 100644 --- a/ICSharpCode.ILSpyX/Analyzers/Builtin/TypeExposedByAnalyzer.cs +++ b/ICSharpCode.ILSpyX/Analyzers/Builtin/TypeExposedByAnalyzer.cs @@ -22,12 +22,15 @@ using System.Diagnostics; namespace ICSharpCode.ILSpyX.Analyzers.Builtin { + using System.ComponentModel.Composition; + 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; diff --git a/ICSharpCode.ILSpyX/Analyzers/Builtin/TypeExtensionMethodsAnalyzer.cs b/ICSharpCode.ILSpyX/Analyzers/Builtin/TypeExtensionMethodsAnalyzer.cs index b55711021..2e28cd526 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 603f3e5b0..03bf39155 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 e4a10801f..b7a8a4147 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/ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs b/ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs index 32f71afa9..ecb1e25d1 100644 --- a/ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs +++ b/ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs @@ -32,6 +32,7 @@ using ICSharpCode.ILSpyX.Abstractions; namespace ILSpy.BamlDecompiler { [Export(typeof(IResourceNodeFactory))] + [PartCreationPolicy(CreationPolicy.Shared)] public sealed class BamlResourceNodeFactory : IResourceNodeFactory { public ITreeNode CreateNode(Resource resource) @@ -44,6 +45,7 @@ namespace ILSpy.BamlDecompiler } [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 cadd1217b..410e49b67 100644 --- a/ILSpy.ReadyToRun/ReadyToRunLanguage.cs +++ b/ILSpy.ReadyToRun/ReadyToRunLanguage.cs @@ -95,6 +95,7 @@ namespace ICSharpCode.ILSpy.ReadyToRun #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 53da7a71f..fbe046ea4 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 @@ using ICSharpCode.ILSpyX.Settings; 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/AboutPage.cs b/ILSpy/AboutPage.cs index b796fa362..43303bf88 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 @@ using ICSharpCode.ILSpyX.Settings; 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 e221235e8..854aa565c 100644 --- a/ILSpy/Analyzers/AnalyzeCommand.cs +++ b/ILSpy/Analyzers/AnalyzeCommand.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.ComponentModel.Composition; using System.Linq; using ICSharpCode.Decompiler.Metadata; @@ -27,6 +28,7 @@ using ICSharpCode.ILSpy.TreeNodes; namespace ICSharpCode.ILSpy.Analyzers { [ExportContextMenuEntry(Header = nameof(Resources.Analyze), Icon = "Images/Search", Category = nameof(Resources.Analyze), InputGestureText = "Ctrl+R", Order = 100)] + [PartCreationPolicy(CreationPolicy.Shared)] internal sealed class AnalyzeCommand : SimpleCommand, IContextMenuEntry { public bool IsVisible(TextViewContext context) diff --git a/ILSpy/Analyzers/CopyAnalysisResultsContextMenuEntry.cs b/ILSpy/Analyzers/CopyAnalysisResultsContextMenuEntry.cs index 1e2566b68..5d0af2d66 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 @@ using System.Windows; 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 d3ed19a46..662f466de 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/Commands/BrowseBackCommand.cs b/ILSpy/Commands/BrowseBackCommand.cs index bb8580495..cf424cbd4 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 @@ using ICSharpCode.ILSpy.Properties; 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 5935bd666..3a8276457 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 @@ using ICSharpCode.ILSpy.Properties; 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 011d68241..58438c604 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 0a197035c..47490e37b 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 @@ using ICSharpCode.ILSpy.TreeNodes; 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 7bc61a167..b6ccb0bd6 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; @@ -34,6 +35,7 @@ using TomsToolbox.Essentials; 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,6 +88,7 @@ namespace ICSharpCode.ILSpy } [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) diff --git a/ILSpy/Commands/DecompileCommand.cs b/ILSpy/Commands/DecompileCommand.cs index ff0a995df..0a7d1a385 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 @@ using ICSharpCode.ILSpy.TreeNodes; 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 744c1ecc9..07e9bebcb 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 @@ using ICSharpCode.ILSpy.TreeNodes; 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 34f7f8575..fc44d405a 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 @@ using ICSharpCode.ILSpyX; 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 bbb68122c..fb475ef01 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/ExtractPackageEntryContextMenuEntry.cs b/ILSpy/Commands/ExtractPackageEntryContextMenuEntry.cs index 57076e517..3f22fa1cd 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 @@ using Microsoft.Win32; 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 773c7cd4c..b7c3997d0 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 @@ using Microsoft.Win32; namespace ICSharpCode.ILSpy { [ExportContextMenuEntry(Header = nameof(Resources.GeneratePortable))] + [PartCreationPolicy(CreationPolicy.Shared)] class GeneratePdbContextMenuEntry : IContextMenuEntry { public void Execute(TextViewContext context) @@ -103,6 +105,7 @@ namespace ICSharpCode.ILSpy } [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 ec58ff59d..0f2240e14 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 a1663a499..7c66ac43a 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 a077ceb9d..393e00eeb 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 f64478feb..c69777370 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 @@ using Microsoft.DiaSymReader.Tools; 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 @@ namespace ICSharpCode.ILSpy } [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 3e188a7e6..cfb03bf34 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 a2a4430be..c8696104b 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 @@ using ICSharpCode.ILSpy.Properties; 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 @@ namespace ICSharpCode.ILSpy } [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 5ce79ee10..897e6f12c 100644 --- a/ILSpy/Commands/SaveCodeContextMenuEntry.cs +++ b/ILSpy/Commands/SaveCodeContextMenuEntry.cs @@ -30,10 +30,12 @@ using ICSharpCode.ILSpy.ViewModels; using Microsoft.Win32; using ICSharpCode.ILSpyX.TreeView; +using System.ComponentModel.Composition; 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) diff --git a/ILSpy/Commands/SaveCommand.cs b/ILSpy/Commands/SaveCommand.cs index 9f07f518f..8b8871e0a 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 @@ using ICSharpCode.ILSpy.Properties; 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 1ec6ef73b..3d8a10397 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 @@ using ICSharpCode.ILSpy.TreeNodes; 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 11a411ed6..c6be0a9db 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 @@ using ICSharpCode.ILSpy.TreeNodes; 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 08c7f5a42..a49f1c456 100644 --- a/ILSpy/Commands/SearchMsdnContextMenuEntry.cs +++ b/ILSpy/Commands/SearchMsdnContextMenuEntry.cs @@ -23,9 +23,12 @@ using ICSharpCode.ILSpy.Properties; 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 1d8da975f..061ff7e6b 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 @@ using Microsoft.Win32; 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/ShowCFGContextMenuEntry.cs b/ILSpy/Commands/ShowCFGContextMenuEntry.cs index a3e2b4e29..b4e8be003 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 110a60b8a..7621778a7 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 @@ namespace ICSharpCode.ILSpy [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/Docking/CloseAllDocumentsCommand.cs b/ILSpy/Docking/CloseAllDocumentsCommand.cs index fc33eb8cd..2c0ceb41b 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 @@ using ICSharpCode.ILSpy.Properties; 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 @@ namespace ICSharpCode.ILSpy.Docking } [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/ExportProviderAdapter.cs b/ILSpy/ExportProviderAdapter.cs index 555352fc4..81bc7bfbf 100644 --- a/ILSpy/ExportProviderAdapter.cs +++ b/ILSpy/ExportProviderAdapter.cs @@ -15,7 +15,7 @@ namespace ICSharpCode.ILSpy; /// /// Adapter for Microsoft.VisualStudio.Composition. to . /// -public class ExportProviderAdapter : IExportProvider +public sealed class ExportProviderAdapter : IExportProvider { private static readonly Type DefaultMetadataType = typeof(Dictionary); @@ -42,10 +42,7 @@ public class ExportProviderAdapter : IExportProvider return _exportProvider.GetExportedValues(contractName).SingleOrDefault(); } -#pragma warning disable CS8769 // Nullability of reference types in type of parameter doesn't match implemented member (possibly because of nullability attributes). - // can't apply NotNullWhen here, because ICSharpCode.Decompiler defines a duplicate attribute, and uses InternalsVisibleTo("ILSpy"), so this attribute is now ambiguous! - bool IExportProvider.TryGetExportedValue(string? contractName, /*[NotNullWhen(true)]*/ out T? value) where T : class -#pragma warning restore CS8769 // Nullability of reference types in type of parameter doesn't match implemented member (possibly because of nullability attributes). + bool IExportProvider.TryGetExportedValue(string? contractName, [NotNullWhen(true)] out T? value) where T : class { value = _exportProvider.GetExportedValues(contractName).SingleOrDefault(); diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index 5bbadbe1b..b7657c891 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -50,7 +50,6 @@ - @@ -92,8 +91,8 @@ - - + + diff --git a/ILSpy/Languages/CSharpILMixedLanguage.cs b/ILSpy/Languages/CSharpILMixedLanguage.cs index 6ad3bb54e..31975cc24 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 6e8296241..dcf7e91a2 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 95c881bb4..ab8f89ab7 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/Metadata/GoToTokenCommand.cs b/ILSpy/Metadata/GoToTokenCommand.cs index ae0fa1657..1c0caca2b 100644 --- a/ILSpy/Metadata/GoToTokenCommand.cs +++ b/ILSpy/Metadata/GoToTokenCommand.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.ComponentModel.Composition; using System.Linq; using System.Reflection; using System.Reflection.Metadata.Ecma335; @@ -31,6 +32,7 @@ using ICSharpCode.ILSpy.Properties; namespace ICSharpCode.ILSpy.Commands { [ExportContextMenuEntry(Header = nameof(Resources.GoToToken), Order = 10)] + [PartCreationPolicy(CreationPolicy.Shared)] class GoToTokenCommand : IContextMenuEntry { public void Execute(TextViewContext context) @@ -68,6 +70,7 @@ namespace ICSharpCode.ILSpy.Commands } [ExportContextMenuEntry(Header = nameof(Resources.Copy), Order = 10)] + [PartCreationPolicy(CreationPolicy.Shared)] class CopyCommand : IContextMenuEntry { public void Execute(TextViewContext context) diff --git a/ILSpy/Metadata/MetadataProtocolHandler.cs b/ILSpy/Metadata/MetadataProtocolHandler.cs index e4fc4bbec..c49e300d7 100644 --- a/ILSpy/Metadata/MetadataProtocolHandler.cs +++ b/ILSpy/Metadata/MetadataProtocolHandler.cs @@ -26,6 +26,7 @@ using ICSharpCode.ILSpy.TreeNodes; namespace ICSharpCode.ILSpy.Metadata { [Export(typeof(IProtocolHandler))] + [PartCreationPolicy(CreationPolicy.Shared)] class MetadataProtocolHandler : IProtocolHandler { public ILSpyTreeNode Resolve(string protocol, MetadataFile module, Handle handle, out bool newTabPage) diff --git a/ILSpy/Options/DecompilerSettingsPanel.xaml.cs b/ILSpy/Options/DecompilerSettingsPanel.xaml.cs index 9f542f243..8a55d73fb 100644 --- a/ILSpy/Options/DecompilerSettingsPanel.xaml.cs +++ b/ILSpy/Options/DecompilerSettingsPanel.xaml.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.Xml.Linq; using ICSharpCode.ILSpyX.Settings; @@ -26,6 +27,7 @@ namespace ICSharpCode.ILSpy.Options /// Interaction logic for DecompilerSettingsPanel.xaml /// [ExportOptionPage(Title = nameof(Properties.Resources.Decompiler), Order = 10)] + [PartCreationPolicy(CreationPolicy.NonShared)] internal partial class DecompilerSettingsPanel : IOptionPage { public DecompilerSettingsPanel() diff --git a/ILSpy/Options/DisplaySettingsPanel.xaml.cs b/ILSpy/Options/DisplaySettingsPanel.xaml.cs index 908ed3501..0319b327d 100644 --- a/ILSpy/Options/DisplaySettingsPanel.xaml.cs +++ b/ILSpy/Options/DisplaySettingsPanel.xaml.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.ComponentModel.Composition; using System.Linq; using System.Threading.Tasks; using System.Windows; @@ -34,6 +35,7 @@ namespace ICSharpCode.ILSpy.Options /// Interaction logic for DisplaySettingsPanel.xaml /// [ExportOptionPage(Title = nameof(Properties.Resources.Display), Order = 20)] + [PartCreationPolicy(CreationPolicy.NonShared)] public partial class DisplaySettingsPanel : UserControl, IOptionPage { public DisplaySettingsPanel() diff --git a/ILSpy/Options/MiscSettingsPanel.xaml.cs b/ILSpy/Options/MiscSettingsPanel.xaml.cs index f7c49c14e..ac98981dd 100644 --- a/ILSpy/Options/MiscSettingsPanel.xaml.cs +++ b/ILSpy/Options/MiscSettingsPanel.xaml.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.Controls; using System.Xml.Linq; @@ -27,6 +28,7 @@ namespace ICSharpCode.ILSpy.Options /// Interaction logic for MiscSettingsPanel.xaml /// [ExportOptionPage(Title = nameof(Properties.Resources.Misc), Order = 30)] + [PartCreationPolicy(CreationPolicy.NonShared)] public partial class MiscSettingsPanel : UserControl, IOptionPage { public MiscSettingsPanel() diff --git a/ILSpy/Options/OptionsDialog.xaml.cs b/ILSpy/Options/OptionsDialog.xaml.cs index ab15a93d5..72413f672 100644 --- a/ILSpy/Options/OptionsDialog.xaml.cs +++ b/ILSpy/Options/OptionsDialog.xaml.cs @@ -121,6 +121,7 @@ namespace ICSharpCode.ILSpy.Options } [ExportMainMenuCommand(ParentMenuID = nameof(Resources._View), Header = nameof(Resources._Options), MenuCategory = nameof(Resources.Options), MenuOrder = 999)] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class ShowOptionsCommand : SimpleCommand { public override void Execute(object parameter) diff --git a/ILSpy/Search/SearchPane.cs b/ILSpy/Search/SearchPane.cs index 55a29370c..2f7d25d3f 100644 --- a/ILSpy/Search/SearchPane.cs +++ b/ILSpy/Search/SearchPane.cs @@ -21,6 +21,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; +using System.ComponentModel.Composition; using System.Diagnostics; using System.Linq; using System.Text.RegularExpressions; @@ -531,6 +532,7 @@ namespace ICSharpCode.ILSpy.Search } [ExportToolbarCommand(ToolTip = nameof(Properties.Resources.SearchCtrlShiftFOrCtrlE), ToolbarIcon = "Images/Search", ToolbarCategory = nameof(Properties.Resources.View), ToolbarOrder = 100)] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class ShowSearchCommand : CommandWrapper { public ShowSearchCommand() diff --git a/ILSpy/TextView/EditorCommands.cs b/ILSpy/TextView/EditorCommands.cs index 52784203b..0f6b5e646 100644 --- a/ILSpy/TextView/EditorCommands.cs +++ b/ILSpy/TextView/EditorCommands.cs @@ -16,11 +16,14 @@ // 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.TextView { [ExportContextMenuEntry(Header = nameof(Resources.Copy), Category = nameof(Resources.Editor))] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class CopyContextMenuEntry : IContextMenuEntry { public bool IsVisible(TextViewContext context) @@ -40,6 +43,7 @@ namespace ICSharpCode.ILSpy.TextView } [ExportContextMenuEntry(Header = nameof(Resources.Select), Category = nameof(Resources.Editor))] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class SelectAllContextMenuEntry : IContextMenuEntry { public bool IsVisible(TextViewContext context) diff --git a/ILSpy/TextView/FoldingCommands.cs b/ILSpy/TextView/FoldingCommands.cs index dc8051631..c061c543e 100644 --- a/ILSpy/TextView/FoldingCommands.cs +++ b/ILSpy/TextView/FoldingCommands.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.AvalonEdit; @@ -25,6 +26,7 @@ using ICSharpCode.ILSpy.Properties; namespace ICSharpCode.ILSpy.TextView { [ExportContextMenuEntryAttribute(Header = nameof(Resources.ToggleFolding), Category = nameof(Resources.Folding))] + [PartCreationPolicy(CreationPolicy.Shared)] internal sealed class ToggleAllContextMenuEntry : IContextMenuEntry { public bool IsVisible(TextViewContext context) @@ -61,6 +63,7 @@ namespace ICSharpCode.ILSpy.TextView } [ExportContextMenuEntryAttribute(Header = nameof(Resources._ToggleFolding), Category = nameof(Resources.Folding))] + [PartCreationPolicy(CreationPolicy.Shared)] internal sealed class ToggleContextMenuEntry : IContextMenuEntry { public bool IsVisible(TextViewContext context) diff --git a/ILSpy/TreeNodes/AssemblyTreeNode.cs b/ILSpy/TreeNodes/AssemblyTreeNode.cs index dc4de23bd..a92a98362 100644 --- a/ILSpy/TreeNodes/AssemblyTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyTreeNode.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel.Composition; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -577,6 +578,7 @@ namespace ICSharpCode.ILSpy.TreeNodes } [ExportContextMenuEntry(Header = nameof(Resources._Remove), Icon = "images/Delete")] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class RemoveAssembly : IContextMenuEntry { public bool IsVisible(TextViewContext context) @@ -603,6 +605,7 @@ namespace ICSharpCode.ILSpy.TreeNodes } [ExportContextMenuEntry(Header = nameof(Resources._Reload), Icon = "images/Refresh")] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class ReloadAssembly : IContextMenuEntry { public bool IsVisible(TextViewContext context) @@ -637,6 +640,7 @@ namespace ICSharpCode.ILSpy.TreeNodes } [ExportContextMenuEntry(Header = nameof(Resources._LoadDependencies), Category = nameof(Resources.Dependencies))] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class LoadDependencies : IContextMenuEntry { public bool IsVisible(TextViewContext context) @@ -676,6 +680,7 @@ namespace ICSharpCode.ILSpy.TreeNodes } [ExportContextMenuEntry(Header = nameof(Resources._AddMainList), Category = nameof(Resources.Dependencies))] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class AddToMainList : IContextMenuEntry { public bool IsVisible(TextViewContext context) @@ -710,6 +715,7 @@ namespace ICSharpCode.ILSpy.TreeNodes } [ExportContextMenuEntry(Header = nameof(Resources._OpenContainingFolder), Category = nameof(Resources.Shell))] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class OpenContainingFolder : IContextMenuEntry { public bool IsVisible(TextViewContext context) @@ -762,6 +768,7 @@ namespace ICSharpCode.ILSpy.TreeNodes } [ExportContextMenuEntry(Header = nameof(Resources._OpenCommandLineHere), Category = nameof(Resources.Shell))] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class OpenCmdHere : IContextMenuEntry { public bool IsVisible(TextViewContext context) diff --git a/ILSpy/TreeNodes/ResourceNodes/CursorResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceNodes/CursorResourceEntryNode.cs index b1e86026e..993c22bdf 100644 --- a/ILSpy/TreeNodes/ResourceNodes/CursorResourceEntryNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/CursorResourceEntryNode.cs @@ -31,6 +31,7 @@ using ICSharpCode.ILSpyX.Abstractions; namespace ICSharpCode.ILSpy.TreeNodes { [Export(typeof(IResourceNodeFactory))] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class CursorResourceNodeFactory : IResourceNodeFactory { static readonly string[] imageFileExtensions = { ".cur" }; diff --git a/ILSpy/TreeNodes/ResourceNodes/IconResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceNodes/IconResourceEntryNode.cs index 52716233d..075aa1ca0 100644 --- a/ILSpy/TreeNodes/ResourceNodes/IconResourceEntryNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/IconResourceEntryNode.cs @@ -31,6 +31,7 @@ using ICSharpCode.ILSpyX.Abstractions; namespace ICSharpCode.ILSpy.TreeNodes { [Export(typeof(IResourceNodeFactory))] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class IconResourceNodeFactory : IResourceNodeFactory { public ITreeNode CreateNode(Resource resource) diff --git a/ILSpy/TreeNodes/ResourceNodes/ImageListResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceNodes/ImageListResourceEntryNode.cs index e3646c807..7a3d9c795 100644 --- a/ILSpy/TreeNodes/ResourceNodes/ImageListResourceEntryNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/ImageListResourceEntryNode.cs @@ -28,6 +28,7 @@ using ICSharpCode.ILSpyX.Abstractions; namespace ICSharpCode.ILSpy.TreeNodes { [Export(typeof(IResourceNodeFactory))] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class ImageListResourceEntryNodeFactory : IResourceNodeFactory { public ITreeNode CreateNode(Resource resource) diff --git a/ILSpy/TreeNodes/ResourceNodes/ImageResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceNodes/ImageResourceEntryNode.cs index 719653af1..cad4098eb 100644 --- a/ILSpy/TreeNodes/ResourceNodes/ImageResourceEntryNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/ImageResourceEntryNode.cs @@ -31,6 +31,7 @@ using ICSharpCode.ILSpyX.Abstractions; namespace ICSharpCode.ILSpy.TreeNodes { [Export(typeof(IResourceNodeFactory))] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class ImageResourceNodeFactory : IResourceNodeFactory { static readonly string[] imageFileExtensions = { ".png", ".gif", ".bmp", ".jpg" }; diff --git a/ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs b/ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs index 5c0548f73..324390af2 100644 --- a/ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs @@ -38,6 +38,7 @@ using Microsoft.Win32; namespace ICSharpCode.ILSpy.TreeNodes { [Export(typeof(IResourceNodeFactory))] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class ResourcesFileTreeNodeFactory : IResourceNodeFactory { public ITreeNode CreateNode(Resource resource) diff --git a/ILSpy/TreeNodes/ResourceNodes/XamlResourceNode.cs b/ILSpy/TreeNodes/ResourceNodes/XamlResourceNode.cs index 5faa3e186..e017948df 100644 --- a/ILSpy/TreeNodes/ResourceNodes/XamlResourceNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/XamlResourceNode.cs @@ -31,6 +31,7 @@ using ICSharpCode.ILSpyX.Abstractions; namespace ICSharpCode.ILSpy.Xaml { [Export(typeof(IResourceNodeFactory))] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class XamlResourceNodeFactory : IResourceNodeFactory { public ITreeNode CreateNode(Resource resource) diff --git a/ILSpy/TreeNodes/ResourceNodes/XmlResourceNode.cs b/ILSpy/TreeNodes/ResourceNodes/XmlResourceNode.cs index cdc154b90..740947602 100644 --- a/ILSpy/TreeNodes/ResourceNodes/XmlResourceNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/XmlResourceNode.cs @@ -31,6 +31,7 @@ using ICSharpCode.ILSpyX.Abstractions; namespace ICSharpCode.ILSpy.Xaml { [Export(typeof(IResourceNodeFactory))] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class XmlResourceNodeFactory : IResourceNodeFactory { private readonly static string[] xmlFileExtensions = { ".xml", ".xsd", ".xslt" }; diff --git a/ILSpy/TreeNodes/ThreadingSupport.cs b/ILSpy/TreeNodes/ThreadingSupport.cs index 636a967f3..7f99b3afb 100644 --- a/ILSpy/TreeNodes/ThreadingSupport.cs +++ b/ILSpy/TreeNodes/ThreadingSupport.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel.Composition; using System.Diagnostics; using System.Linq; using System.Text; @@ -171,6 +172,7 @@ namespace ICSharpCode.ILSpy.TreeNodes } [ExportContextMenuEntry(Header = nameof(Resources.CopyErrorMessage))] + [PartCreationPolicy(CreationPolicy.Shared)] sealed class CopyErrorMessageContextMenu : IContextMenuEntry { public bool IsVisible(TextViewContext context) diff --git a/ILSpy/ViewModels/AnalyzerPaneModel.cs b/ILSpy/ViewModels/AnalyzerPaneModel.cs index a91f27e3f..557113683 100644 --- a/ILSpy/ViewModels/AnalyzerPaneModel.cs +++ b/ILSpy/ViewModels/AnalyzerPaneModel.cs @@ -16,17 +16,19 @@ // 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 System.Windows.Input; namespace ICSharpCode.ILSpy.ViewModels { [ExportToolPane(ContentId = PaneContentId)] + [PartCreationPolicy(CreationPolicy.Shared)] public class AnalyzerPaneModel : ToolPaneModel { public const string PaneContentId = "analyzerPane"; - private AnalyzerPaneModel() + public AnalyzerPaneModel() { ContentId = PaneContentId; Title = Properties.Resources.Analyze; diff --git a/ILSpy/ViewModels/AssemblyListPaneModel.cs b/ILSpy/ViewModels/AssemblyListPaneModel.cs index 38439467a..bfbc8fa02 100644 --- a/ILSpy/ViewModels/AssemblyListPaneModel.cs +++ b/ILSpy/ViewModels/AssemblyListPaneModel.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; using System.Windows.Input; @@ -24,11 +25,12 @@ using ICSharpCode.ILSpy.Properties; namespace ICSharpCode.ILSpy.ViewModels { [ExportToolPane(ContentId = PaneContentId)] + [PartCreationPolicy(CreationPolicy.Shared)] public class AssemblyListPaneModel : ToolPaneModel { public const string PaneContentId = "assemblyListPane"; - private AssemblyListPaneModel() + public AssemblyListPaneModel() { Title = Resources.Assemblies; ContentId = PaneContentId; diff --git a/ILSpy/ViewModels/DebugStepsPaneModel.cs b/ILSpy/ViewModels/DebugStepsPaneModel.cs index 6741f2aa5..50a169041 100644 --- a/ILSpy/ViewModels/DebugStepsPaneModel.cs +++ b/ILSpy/ViewModels/DebugStepsPaneModel.cs @@ -16,18 +16,20 @@ // 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; namespace ICSharpCode.ILSpy.ViewModels { #if DEBUG [ExportToolPane(ContentId = PaneContentId)] + [PartCreationPolicy(CreationPolicy.Shared)] #endif public class DebugStepsPaneModel : ToolPaneModel { public const string PaneContentId = "debugStepsPane"; - private DebugStepsPaneModel() + public DebugStepsPaneModel() { ContentId = PaneContentId; Title = Properties.Resources.DebugSteps; diff --git a/ILSpy/ViewModels/SearchPaneModel.cs b/ILSpy/ViewModels/SearchPaneModel.cs index 04cacb2c1..e9e0d30d5 100644 --- a/ILSpy/ViewModels/SearchPaneModel.cs +++ b/ILSpy/ViewModels/SearchPaneModel.cs @@ -16,17 +16,19 @@ // 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 System.Windows.Input; namespace ICSharpCode.ILSpy.ViewModels { [ExportToolPane(ContentId = PaneContentId)] + [PartCreationPolicy(CreationPolicy.Shared)] public class SearchPaneModel : ToolPaneModel { public const string PaneContentId = "searchPane"; - private SearchPaneModel() + public SearchPaneModel() { ContentId = PaneContentId; Title = Properties.Resources.SearchPane_Search; diff --git a/TestPlugin/AboutPageAddition.cs b/TestPlugin/AboutPageAddition.cs index 71e6eb388..4088b2f92 100644 --- a/TestPlugin/AboutPageAddition.cs +++ b/TestPlugin/AboutPageAddition.cs @@ -11,6 +11,7 @@ using ICSharpCode.ILSpy; namespace TestPlugin { [Export(typeof(IAboutPageAddition))] + [PartCreationPolicy(CreationPolicy.Shared)] public class AboutPageAddition : IAboutPageAddition { public void Write(ISmartTextOutput textOutput) diff --git a/TestPlugin/ContextMenuCommand.cs b/TestPlugin/ContextMenuCommand.cs index 9dc672fe5..dad7dc3f6 100644 --- a/TestPlugin/ContextMenuCommand.cs +++ b/TestPlugin/ContextMenuCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // This code is distributed under MIT X11 license (for details please see \doc\license.txt) +using System.ComponentModel.Composition; using System.Linq; using ICSharpCode.ILSpy; @@ -9,6 +10,7 @@ using ICSharpCode.ILSpy.TreeNodes; namespace TestPlugin { [ExportContextMenuEntryAttribute(Header = "_Save Assembly")] + [PartCreationPolicy(CreationPolicy.Shared)] public class SaveAssembly : IContextMenuEntry { public bool IsVisible(TextViewContext context) diff --git a/TestPlugin/CustomLanguage.cs b/TestPlugin/CustomLanguage.cs index e41ea015e..7e38fdffe 100644 --- a/TestPlugin/CustomLanguage.cs +++ b/TestPlugin/CustomLanguage.cs @@ -15,6 +15,7 @@ namespace TestPlugin /// Adds a new language to the decompiler. /// [Export(typeof(Language))] + [PartCreationPolicy(CreationPolicy.Shared)] public class CustomLanguage : Language { public override string Name { diff --git a/TestPlugin/CustomOptionPage.xaml.cs b/TestPlugin/CustomOptionPage.xaml.cs index 86adf9495..9c38af296 100644 --- a/TestPlugin/CustomOptionPage.xaml.cs +++ b/TestPlugin/CustomOptionPage.xaml.cs @@ -2,6 +2,7 @@ // This code is distributed under MIT X11 license (for details please see \doc\license.txt) using System.ComponentModel; +using System.ComponentModel.Composition; using System.Windows.Controls; using System.Xml.Linq; @@ -12,6 +13,7 @@ using ICSharpCode.ILSpyX.Settings; namespace TestPlugin { [ExportOptionPage(Title = "TestPlugin", Order = 0)] + [PartCreationPolicy(CreationPolicy.NonShared)] partial class CustomOptionPage : UserControl, IOptionPage { static readonly XNamespace ns = "http://www.ilspy.net/testplugin"; diff --git a/TestPlugin/MainMenuCommand.cs b/TestPlugin/MainMenuCommand.cs index 501d43b1b..bb4a041f8 100644 --- a/TestPlugin/MainMenuCommand.cs +++ b/TestPlugin/MainMenuCommand.cs @@ -1,6 +1,8 @@ // Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // This code is distributed under MIT X11 license (for details please see \doc\license.txt) +using System.ComponentModel.Composition; + using ICSharpCode.ILSpy; namespace TestPlugin @@ -16,6 +18,7 @@ namespace TestPlugin // ToolbarCategory: optional, used for grouping related toolbar items together. A separator is added between different groups. // ToolbarOrder: controls the order in which the items appear (items are sorted by this value) [ExportToolbarCommand(ToolTip = "Clears the current assembly list", ToolbarIcon = "Clear.png", ToolbarCategory = "Open", ToolbarOrder = 1.5)] + [PartCreationPolicy(CreationPolicy.Shared)] public class UnloadAllAssembliesCommand : SimpleCommand { public override void Execute(object parameter) From 00ff9c8816fcc4771bc29f04879cc299a8041b81 Mon Sep 17 00:00:00 2001 From: tom-englert Date: Fri, 16 Aug 2024 13:40:12 +0200 Subject: [PATCH 20/35] Remove usage of custom template selector with magic ids, use standard WPF pattern to locate data templates --- ILSpy/Commands/ExportCommandAttribute.cs | 10 +---- ILSpy/Docking/PaneTemplateSelector.cs | 48 ----------------------- ILSpy/MainWindow.xaml | 15 ++++--- ILSpy/MainWindow.xaml.cs | 22 +++-------- ILSpy/ViewModels/AnalyzerPaneModel.cs | 5 +-- ILSpy/ViewModels/AssemblyListPaneModel.cs | 4 +- ILSpy/ViewModels/DebugStepsPaneModel.cs | 4 +- ILSpy/ViewModels/LegacyToolPaneModel.cs | 2 - ILSpy/ViewModels/SearchPaneModel.cs | 4 +- ILSpy/ViewModels/ToolPaneModel.cs | 3 -- 10 files changed, 21 insertions(+), 96 deletions(-) delete mode 100644 ILSpy/Docking/PaneTemplateSelector.cs diff --git a/ILSpy/Commands/ExportCommandAttribute.cs b/ILSpy/Commands/ExportCommandAttribute.cs index 282774bff..19d100fd4 100644 --- a/ILSpy/Commands/ExportCommandAttribute.cs +++ b/ILSpy/Commands/ExportCommandAttribute.cs @@ -99,21 +99,15 @@ namespace ICSharpCode.ILSpy #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/Docking/PaneTemplateSelector.cs b/ILSpy/Docking/PaneTemplateSelector.cs deleted file mode 100644 index 7890a1dde..000000000 --- a/ILSpy/Docking/PaneTemplateSelector.cs +++ /dev/null @@ -1,48 +0,0 @@ -// 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.ObjectModel; -using System.Linq; -using System.Windows; -using System.Windows.Controls; - -namespace ICSharpCode.ILSpy.Docking -{ - public class TemplateMapping - { - public Type Type { get; set; } - public DataTemplate Template { get; set; } - } - - public class PaneTemplateSelector : DataTemplateSelector - { - public Collection Mappings { get; set; } = new Collection(); - - public override DataTemplate SelectTemplate(object item, DependencyObject container) - { - if (item == null) - { - return base.SelectTemplate(item, container); - } - - return Mappings.FirstOrDefault(m => m.Type == item.GetType())?.Template - ?? base.SelectTemplate(item, container); - } - } -} diff --git a/ILSpy/MainWindow.xaml b/ILSpy/MainWindow.xaml index 6f423f315..7f6831651 100644 --- a/ILSpy/MainWindow.xaml +++ b/ILSpy/MainWindow.xaml @@ -23,6 +23,7 @@ xmlns:b="http://schemas.microsoft.com/xaml/behaviors" xmlns:themes="clr-namespace:ICSharpCode.ILSpy.Themes" xmlns:toms="urn:TomsToolbox" + xmlns:viewModels="clr-namespace:ICSharpCode.ILSpy.ViewModels" d:DataContext="{d:DesignInstance local:MainWindowViewModel}" > @@ -69,29 +70,33 @@ - + - + - + - + - + + + + + diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index 2f8ec46f7..26f04b11c 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -58,6 +58,7 @@ using ICSharpCode.ILSpyX; using ICSharpCode.ILSpyX.FileLoaders; using ICSharpCode.ILSpyX.Settings; using ICSharpCode.ILSpy.Controls.TreeView; +using ICSharpCode.ILSpyX.Extensions; using Microsoft.Win32; using ICSharpCode.ILSpyX.TreeView; @@ -365,25 +366,12 @@ namespace ICSharpCode.ILSpy #endregion #region Tool Pane extensibility + private void InitToolPanes() { - var toolPanes = App.ExportProvider.GetExports("ToolPane"); - var templateSelector = new PaneTemplateSelector(); - templateSelector.Mappings.Add(new TemplateMapping { - Type = typeof(TabPageModel), - Template = (DataTemplate)FindResource("DefaultContentTemplate") - }); - templateSelector.Mappings.Add(new TemplateMapping { - Type = typeof(LegacyToolPaneModel), - Template = (DataTemplate)FindResource("DefaultContentTemplate") - }); - foreach (var toolPane in toolPanes) - { - ToolPaneModel model = toolPane.Value; - templateSelector.Mappings.Add(new TemplateMapping { Type = model.GetType(), Template = model.Template }); - DockWorkspace.Instance.ToolPanes.Add(model); - } - DockManager.LayoutItemTemplateSelector = templateSelector; + var toolPanes = App.ExportProvider.GetExportedValues("ToolPane"); + + DockWorkspace.Instance.ToolPanes.AddRange(toolPanes); } private void InitWindowMenu() diff --git a/ILSpy/ViewModels/AnalyzerPaneModel.cs b/ILSpy/ViewModels/AnalyzerPaneModel.cs index 557113683..ca92e5dae 100644 --- a/ILSpy/ViewModels/AnalyzerPaneModel.cs +++ b/ILSpy/ViewModels/AnalyzerPaneModel.cs @@ -17,12 +17,11 @@ // DEALINGS IN THE SOFTWARE. using System.ComponentModel.Composition; -using System.Windows; using System.Windows.Input; namespace ICSharpCode.ILSpy.ViewModels { - [ExportToolPane(ContentId = PaneContentId)] + [ExportToolPane] [PartCreationPolicy(CreationPolicy.Shared)] public class AnalyzerPaneModel : ToolPaneModel { @@ -35,7 +34,5 @@ namespace ICSharpCode.ILSpy.ViewModels ShortcutKey = new KeyGesture(Key.R, ModifierKeys.Control); AssociatedCommand = ILSpyCommands.Analyze; } - - public override DataTemplate Template => (DataTemplate)MainWindow.Instance.FindResource("AnalyzerPaneTemplate"); } } diff --git a/ILSpy/ViewModels/AssemblyListPaneModel.cs b/ILSpy/ViewModels/AssemblyListPaneModel.cs index bfbc8fa02..d6156cf8f 100644 --- a/ILSpy/ViewModels/AssemblyListPaneModel.cs +++ b/ILSpy/ViewModels/AssemblyListPaneModel.cs @@ -24,7 +24,7 @@ using ICSharpCode.ILSpy.Properties; namespace ICSharpCode.ILSpy.ViewModels { - [ExportToolPane(ContentId = PaneContentId)] + [ExportToolPane] [PartCreationPolicy(CreationPolicy.Shared)] public class AssemblyListPaneModel : ToolPaneModel { @@ -37,7 +37,5 @@ namespace ICSharpCode.ILSpy.ViewModels IsCloseable = false; ShortcutKey = new KeyGesture(Key.F6); } - - public override DataTemplate Template => (DataTemplate)MainWindow.Instance.FindResource("AssemblyListPaneTemplate"); } } diff --git a/ILSpy/ViewModels/DebugStepsPaneModel.cs b/ILSpy/ViewModels/DebugStepsPaneModel.cs index 50a169041..406f047a8 100644 --- a/ILSpy/ViewModels/DebugStepsPaneModel.cs +++ b/ILSpy/ViewModels/DebugStepsPaneModel.cs @@ -22,7 +22,7 @@ using System.Windows; namespace ICSharpCode.ILSpy.ViewModels { #if DEBUG - [ExportToolPane(ContentId = PaneContentId)] + [ExportToolPane] [PartCreationPolicy(CreationPolicy.Shared)] #endif public class DebugStepsPaneModel : ToolPaneModel @@ -34,7 +34,5 @@ namespace ICSharpCode.ILSpy.ViewModels ContentId = PaneContentId; Title = Properties.Resources.DebugSteps; } - - public override DataTemplate Template => (DataTemplate)MainWindow.Instance.FindResource("DebugStepsPaneTemplate"); } } diff --git a/ILSpy/ViewModels/LegacyToolPaneModel.cs b/ILSpy/ViewModels/LegacyToolPaneModel.cs index 4cd2a6bbd..8501b43e3 100644 --- a/ILSpy/ViewModels/LegacyToolPaneModel.cs +++ b/ILSpy/ViewModels/LegacyToolPaneModel.cs @@ -39,8 +39,6 @@ namespace ICSharpCode.ILSpy.ViewModels public object Content { get; } - public override DataTemplate Template => throw new NotSupportedException(); - public LegacyToolPaneLocation Location { get; } } } diff --git a/ILSpy/ViewModels/SearchPaneModel.cs b/ILSpy/ViewModels/SearchPaneModel.cs index e9e0d30d5..ea6fca679 100644 --- a/ILSpy/ViewModels/SearchPaneModel.cs +++ b/ILSpy/ViewModels/SearchPaneModel.cs @@ -22,7 +22,7 @@ using System.Windows.Input; namespace ICSharpCode.ILSpy.ViewModels { - [ExportToolPane(ContentId = PaneContentId)] + [ExportToolPane] [PartCreationPolicy(CreationPolicy.Shared)] public class SearchPaneModel : ToolPaneModel { @@ -42,7 +42,5 @@ namespace ICSharpCode.ILSpy.ViewModels base.Show(); MainWindow.Instance.SearchPane.Show(); } - - public override DataTemplate Template => (DataTemplate)MainWindow.Instance.FindResource("SearchPaneTemplate"); } } diff --git a/ILSpy/ViewModels/ToolPaneModel.cs b/ILSpy/ViewModels/ToolPaneModel.cs index 15a79250b..43ab349f1 100644 --- a/ILSpy/ViewModels/ToolPaneModel.cs +++ b/ILSpy/ViewModels/ToolPaneModel.cs @@ -16,7 +16,6 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -using System.Windows; using System.Windows.Input; namespace ICSharpCode.ILSpy.ViewModels @@ -29,8 +28,6 @@ namespace ICSharpCode.ILSpy.ViewModels this.IsVisible = true; } - public abstract DataTemplate Template { get; } - public KeyGesture ShortcutKey { get; protected set; } public string Icon { get; protected set; } From b6ad02d1da286bab9bed7276eafee25dd5e0135f Mon Sep 17 00:00:00 2001 From: tom-englert Date: Fri, 16 Aug 2024 13:55:47 +0200 Subject: [PATCH 21/35] Remove yet another copy of the BoolToVisibilityConverter --- ILSpy/Controls/BoolToVisibilityConverter.cs | 44 --------------------- ILSpy/MainWindow.xaml | 8 ++-- ILSpy/Views/DebugSteps.xaml.cs | 1 - 3 files changed, 3 insertions(+), 50 deletions(-) delete mode 100644 ILSpy/Controls/BoolToVisibilityConverter.cs diff --git a/ILSpy/Controls/BoolToVisibilityConverter.cs b/ILSpy/Controls/BoolToVisibilityConverter.cs deleted file mode 100644 index 31211050c..000000000 --- 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/MainWindow.xaml b/ILSpy/MainWindow.xaml index 7f6831651..1f505652b 100644 --- a/ILSpy/MainWindow.xaml +++ b/ILSpy/MainWindow.xaml @@ -19,7 +19,6 @@ TextOptions.TextFormattingMode="Display" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" d:DesignHeight="500" d:DesignWidth="500" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" - xmlns:styles="urn:TomsToolbox.Wpf.Styles" xmlns:b="http://schemas.microsoft.com/xaml/behaviors" xmlns:themes="clr-namespace:ICSharpCode.ILSpy.Themes" xmlns:toms="urn:TomsToolbox" @@ -27,7 +26,6 @@ d:DataContext="{d:DesignInstance local:MainWindowViewModel}" > - @@ -271,7 +269,7 @@ - - - - - - - - - - - - - - - - - - - diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index 26f04b11c..b6f92d433 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -31,7 +31,6 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; -using System.Windows.Interop; using System.Windows.Media; using System.Windows.Navigation; using System.Windows.Threading; @@ -98,15 +97,9 @@ namespace ICSharpCode.ILSpy } } - public AnalyzerTreeView AnalyzerTreeView { - get { - return !IsLoaded ? null : FindResource("AnalyzerTreeView") as AnalyzerTreeView; - } - } - public SearchPane SearchPane { get { - return FindResource("SearchPane") as SearchPane; + return App.ExportProvider.GetExportedValue(); } } diff --git a/ILSpy/Search/SearchPane.cs b/ILSpy/Search/SearchPane.cs index 2f7d25d3f..7f920599f 100644 --- a/ILSpy/Search/SearchPane.cs +++ b/ILSpy/Search/SearchPane.cs @@ -40,11 +40,15 @@ using ICSharpCode.ILSpyX; using ICSharpCode.ILSpyX.Extensions; using ICSharpCode.ILSpyX.Search; +using TomsToolbox.Wpf.Composition.Mef; + namespace ICSharpCode.ILSpy.Search { /// /// Search pane /// + [DataTemplate(typeof(SearchPaneModel))] + [PartCreationPolicy(CreationPolicy.Shared)] public partial class SearchPane : UserControl { const int MAX_RESULTS = 1000; diff --git a/ILSpy/Views/DebugSteps.xaml.cs b/ILSpy/Views/DebugSteps.xaml.cs index dbe96c3f2..2c3e2e2e2 100644 --- a/ILSpy/Views/DebugSteps.xaml.cs +++ b/ILSpy/Views/DebugSteps.xaml.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using System.ComponentModel.Composition; using System.Windows; using System.Windows.Controls; using System.Windows.Input; @@ -9,8 +10,12 @@ using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.ILSpy.Docking; using ICSharpCode.ILSpy.ViewModels; +using TomsToolbox.Wpf.Composition.Mef; + namespace ICSharpCode.ILSpy { + [DataTemplate(typeof(DebugStepsPaneModel))] + [PartCreationPolicy(CreationPolicy.NonShared)] public partial class DebugSteps : UserControl { static readonly ILAstWritingOptions writingOptions = new ILAstWritingOptions { From cc7de5fe6060538b6602982f40c0cdaf2f885590 Mon Sep 17 00:00:00 2001 From: tom-englert Date: Tue, 20 Aug 2024 10:52:52 +0200 Subject: [PATCH 23/35] Introduce message bus to start decoupling components --- .editorconfig | 45 +++++++++++++++ ICSharpCode.ILSpyX/TreeView/SharpTreeNode.cs | 2 +- ILSpy/Analyzers/AnalyzeCommand.cs | 37 ++----------- ILSpy/Analyzers/AnalyzerTreeView.cs | 21 +++---- ILSpy/Docking/DockWorkspace.cs | 53 ++++++++---------- ...eConverter.cs => TabPageGuardConverter.cs} | 18 +++--- ILSpy/MainWindow.xaml | 9 ++- ILSpy/MainWindow.xaml.cs | 44 ++++++--------- ILSpy/Search/SearchPane.cs | 3 +- ILSpy/Util/MessageBus.cs | 55 +++++++++++++++++++ ILSpy/Views/DebugSteps.xaml.cs | 16 ++---- 11 files changed, 179 insertions(+), 124 deletions(-) rename ILSpy/Docking/{ActiveTabPageConverter.cs => TabPageGuardConverter.cs} (73%) create mode 100644 ILSpy/Util/MessageBus.cs diff --git a/.editorconfig b/.editorconfig index 63a46acc9..64d038bd6 100644 --- a/.editorconfig +++ b/.editorconfig @@ -120,6 +120,51 @@ 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 diff --git a/ICSharpCode.ILSpyX/TreeView/SharpTreeNode.cs b/ICSharpCode.ILSpyX/TreeView/SharpTreeNode.cs index 79686bdba..e34675fc3 100644 --- a/ICSharpCode.ILSpyX/TreeView/SharpTreeNode.cs +++ b/ICSharpCode.ILSpyX/TreeView/SharpTreeNode.cs @@ -182,7 +182,7 @@ namespace ICSharpCode.ILSpyX.TreeView #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/Analyzers/AnalyzeCommand.cs b/ILSpy/Analyzers/AnalyzeCommand.cs index 00e6b03db..d6cf8a7e9 100644 --- a/ILSpy/Analyzers/AnalyzeCommand.cs +++ b/ILSpy/Analyzers/AnalyzeCommand.cs @@ -18,27 +18,18 @@ using System.ComponentModel.Composition; using System.Linq; -using System.Windows; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.TreeNodes; -using TomsToolbox.Composition; - namespace ICSharpCode.ILSpy.Analyzers { [ExportContextMenuEntry(Header = nameof(Resources.Analyze), Icon = "Images/Search", Category = nameof(Resources.Analyze), InputGestureText = "Ctrl+R", Order = 100)] [PartCreationPolicy(CreationPolicy.Shared)] internal sealed class AnalyzeCommand : SimpleCommand, IContextMenuEntry { - private static readonly IExport analyzerTreeViewExport = App.ExportProvider.GetExports().Single(); - - private static AnalyzerTreeView AnalyzerTreeView { - get { - return Application.Current?.MainWindow?.IsLoaded != true ? null : analyzerTreeViewExport.Value; - } - } + private static readonly AnalyzerTreeView AnalyzerTreeView = App.ExportProvider.GetExportedValue(); public bool IsVisible(TextViewContext context) { @@ -60,17 +51,13 @@ namespace ICSharpCode.ILSpy.Analyzers .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) { - if (AnalyzerTreeView is null) - { - return; - } if (context.SelectedTreeNodes != null) { foreach (var node in context.SelectedTreeNodes.OfType().ToArray()) @@ -86,25 +73,13 @@ namespace ICSharpCode.ILSpy.Analyzers public override bool CanExecute(object parameter) { - if (AnalyzerTreeView is null) - { - return false; - } - - if (AnalyzerTreeView is { IsKeyboardFocusWithin: true }) - { - return AnalyzerTreeView.SelectedItems.OfType().All(n => n is IMemberTreeNode); - } - - return MainWindow.Instance.SelectedNodes.All(n => n is IMemberTreeNode); + return AnalyzerTreeView.IsKeyboardFocusWithin + ? AnalyzerTreeView.SelectedItems.OfType().All(n => n is IMemberTreeNode) + : MainWindow.Instance.SelectedNodes.All(n => n is IMemberTreeNode); } public override void Execute(object parameter) { - if (AnalyzerTreeView is null) - { - return; - } if (AnalyzerTreeView.IsKeyboardFocusWithin) { foreach (var node in AnalyzerTreeView.SelectedItems.OfType().ToArray()) diff --git a/ILSpy/Analyzers/AnalyzerTreeView.cs b/ILSpy/Analyzers/AnalyzerTreeView.cs index cdc8fcbdb..132f7c1db 100644 --- a/ILSpy/Analyzers/AnalyzerTreeView.cs +++ b/ILSpy/Analyzers/AnalyzerTreeView.cs @@ -29,6 +29,7 @@ using ICSharpCode.ILSpy.Docking; using ICSharpCode.ILSpy.ViewModels; using ICSharpCode.ILSpyX; using ICSharpCode.ILSpy.Controls.TreeView; +using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX.TreeView; using TomsToolbox.Wpf.Composition.Mef; @@ -48,25 +49,21 @@ namespace ICSharpCode.ILSpy.Analyzers 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; + MessageBus.Subscribers += (sender, e) => MainWindow_Instance_CurrentAssemblyListChanged(sender, e); + MessageBus.Subscribers += DockWorkspace_ActiveTabPageChanged; filterSettings = MainWindow.Instance.SessionSettings.FilterSettings; filterSettings.PropertyChanged += FilterSettings_PropertyChanged; } - private void DockWorkspace_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + private void DockWorkspace_ActiveTabPageChanged(object sender, EventArgs e) { - switch (e.PropertyName) - { - case nameof(DockWorkspace.Instance.ActiveTabPage): - filterSettings.PropertyChanged -= FilterSettings_PropertyChanged; - filterSettings = DockWorkspace.Instance.ActiveTabPage.FilterSettings; - filterSettings.PropertyChanged += FilterSettings_PropertyChanged; - break; - } + this.Root ??= new AnalyzerRootNode { Language = MainWindow.Instance.CurrentLanguage }; + + filterSettings.PropertyChanged -= FilterSettings_PropertyChanged; + filterSettings = DockWorkspace.Instance.ActiveTabPage.FilterSettings; + filterSettings.PropertyChanged += FilterSettings_PropertyChanged; } private void FilterSettings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) diff --git a/ILSpy/Docking/DockWorkspace.cs b/ILSpy/Docking/DockWorkspace.cs index 2b1241e65..a1fcd68cd 100644 --- a/ILSpy/Docking/DockWorkspace.cs +++ b/ILSpy/Docking/DockWorkspace.cs @@ -36,23 +36,23 @@ using AvalonDock.Layout.Serialization; using ICSharpCode.AvalonEdit.Highlighting; 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; - - 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) => MainWindow_Instance_CurrentAssemblyListChanged(sender, e); } private void MainWindow_Instance_CurrentAssemblyListChanged(object sender, NotifyCollectionChangedEventArgs e) @@ -118,31 +118,31 @@ namespace ICSharpCode.ILSpy.Docking 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)) + { + return; + } + + var state = value.GetState(); + if (state != null) { - _activeTabPage = value; - 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()); } } @@ -181,11 +181,6 @@ namespace ICSharpCode.ILSpy.Docking } } - protected void RaisePropertyChanged([CallerMemberName] string propertyName = null) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - public void ShowText(AvalonEditTextOutput textOutput) { ActiveTabPage.ShowTextView(textView => textView.ShowText(textOutput)); 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 191a7088a..b6c2d2361 100644 --- a/ILSpy/Docking/ActiveTabPageConverter.cs +++ b/ILSpy/Docking/TabPageGuardConverter.cs @@ -21,24 +21,20 @@ using System.Windows.Data; 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/MainWindow.xaml b/ILSpy/MainWindow.xaml index f2f4bac4f..411ff87b0 100644 --- a/ILSpy/MainWindow.xaml +++ b/ILSpy/MainWindow.xaml @@ -26,8 +26,6 @@ d:DataContext="{d:DesignInstance local:MainWindowViewModel}" > - - + @@ -109,12 +108,15 @@ Command="Search" Executed="SearchCommandExecuted" /> + + + @@ -207,6 +209,7 @@ ItemsSource="{Binding SelectedItem.LanguageVersions, ElementName=languageComboBox, UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding Workspace.ActiveTabPage.FilterSettings.LanguageVersion, UpdateSourceTrigger=PropertyChanged}"/> + @@ -231,7 +234,7 @@ DataContext="{Binding Workspace}" AnchorablesSource="{Binding ToolPanes}" DocumentsSource="{Binding TabPages}" - ActiveContent="{Binding ActiveTabPage, Mode=TwoWay, Converter={StaticResource ActiveTabPageConverter}}" + ActiveContent="{Binding ActiveTabPage, Mode=TwoWay, Converter={docking:TabPageGuardConverter}}" AllowMixedOrientation="True"> diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index b6f92d433..b40d950a7 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -42,7 +42,6 @@ using ICSharpCode.Decompiler.Documentation; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; -using ICSharpCode.ILSpy.Analyzers; using ICSharpCode.ILSpy.AppEnv; using ICSharpCode.ILSpy.Commands; using ICSharpCode.ILSpy.Docking; @@ -57,6 +56,7 @@ using ICSharpCode.ILSpyX; using ICSharpCode.ILSpyX.FileLoaders; using ICSharpCode.ILSpyX.Settings; using ICSharpCode.ILSpy.Controls.TreeView; +using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX.Extensions; using Microsoft.Win32; @@ -130,7 +130,7 @@ namespace ICSharpCode.ILSpy this.Icon = Images.ILSpyIcon; this.DataContext = new MainWindowViewModel { - Workspace = new DockWorkspace(this), + Workspace = DockWorkspace.Instance, SessionSettings = sessionSettings, AssemblyListManager = AssemblyListManager }; @@ -147,7 +147,7 @@ namespace ICSharpCode.ILSpy sessionSettings.PropertyChanged += SessionSettings_PropertyChanged; filterSettings = sessionSettings.FilterSettings; filterSettings.PropertyChanged += filterSettings_PropertyChanged; - DockWorkspace.Instance.PropertyChanged += DockWorkspace_PropertyChanged; + MessageBus.Subscribers += DockWorkspace_ActiveTabPageChanged; InitMainMenu(); InitWindowMenu(); InitToolbar(); @@ -157,25 +157,20 @@ namespace ICSharpCode.ILSpy this.Loaded += MainWindow_Loaded; } - private void DockWorkspace_PropertyChanged(object sender, PropertyChangedEventArgs e) + private void DockWorkspace_ActiveTabPageChanged(object sender, EventArgs e) { - switch (e.PropertyName) - { - case nameof(DockWorkspace.Instance.ActiveTabPage): - DockWorkspace dock = DockWorkspace.Instance; - filterSettings.PropertyChanged -= filterSettings_PropertyChanged; - filterSettings = dock.ActiveTabPage.FilterSettings; - filterSettings.PropertyChanged += filterSettings_PropertyChanged; + DockWorkspace dock = DockWorkspace.Instance; + filterSettings.PropertyChanged -= filterSettings_PropertyChanged; + filterSettings = dock.ActiveTabPage.FilterSettings; + filterSettings.PropertyChanged += filterSettings_PropertyChanged; - var windowMenuItem = mainMenu.Items.OfType().First(m => (string)m.Tag == nameof(Properties.Resources._Window)); - foreach (MenuItem menuItem in windowMenuItem.Items.OfType()) - { - if (menuItem.IsCheckable && menuItem.Tag is TabPageModel) - { - menuItem.IsChecked = menuItem.Tag == dock.ActiveTabPage; - } - } - break; + var windowMenuItem = mainMenu.Items.OfType().First(m => (string)m.Tag == nameof(Properties.Resources._Window)); + foreach (MenuItem menuItem in windowMenuItem.Items.OfType()) + { + if (menuItem.IsCheckable && menuItem.Tag is TabPageModel) + { + menuItem.IsChecked = menuItem.Tag == dock.ActiveTabPage; + } } } @@ -359,7 +354,7 @@ namespace ICSharpCode.ILSpy #endregion #region Tool Pane extensibility - + private void InitToolPanes() { var toolPanes = App.ExportProvider.GetExportedValues("ToolPane"); @@ -632,8 +627,6 @@ namespace ICSharpCode.ILSpy get { return assemblyList; } } - public event NotifyCollectionChangedEventHandler CurrentAssemblyListChanged; - List commandLineLoadedAssemblies = new List(); internal async Task HandleSingleInstanceCommandLineArguments(string[] args) @@ -1038,7 +1031,8 @@ namespace ICSharpCode.ILSpy nd => nd.AncestorsAndSelf().OfType().Any( a => oldAssemblies.Contains(a.LoadedAssembly)))); } - CurrentAssemblyListChanged?.Invoke(this, e); + + MessageBus.Send(this, new CurrentAssemblyListChangedEventArgs(e)); } void LoadInitialAssemblies() @@ -1528,8 +1522,6 @@ namespace ICSharpCode.ILSpy public Language CurrentLanguage => DockWorkspace.Instance.ActiveTabPage.FilterSettings.Language; public LanguageVersion CurrentLanguageVersion => DockWorkspace.Instance.ActiveTabPage.FilterSettings.LanguageVersion; - public bool SupportsLanguageSwitching => DockWorkspace.Instance.ActiveTabPage.SupportsLanguageSwitching; - public event SelectionChangedEventHandler SelectionChanged; public IEnumerable SelectedNodes { diff --git a/ILSpy/Search/SearchPane.cs b/ILSpy/Search/SearchPane.cs index 7f920599f..e35a4da73 100644 --- a/ILSpy/Search/SearchPane.cs +++ b/ILSpy/Search/SearchPane.cs @@ -35,6 +35,7 @@ using System.Windows.Threading; using ICSharpCode.ILSpy.AppEnv; using ICSharpCode.ILSpy.Docking; +using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpy.ViewModels; using ICSharpCode.ILSpyX; using ICSharpCode.ILSpyX.Extensions; @@ -82,7 +83,7 @@ namespace ICSharpCode.ILSpy.Search searchModeComboBox.Items.Add(new { Image = Images.Namespace, Name = "Namespace" }); ContextMenuProvider.Add(listBox); - MainWindow.Instance.CurrentAssemblyListChanged += MainWindow_Instance_CurrentAssemblyListChanged; + MessageBus.Subscribers += (sender, e) => MainWindow_Instance_CurrentAssemblyListChanged(sender, e); filterSettings = MainWindow.Instance.SessionSettings.FilterSettings; CompositionTarget.Rendering += UpdateResults; diff --git a/ILSpy/Util/MessageBus.cs b/ILSpy/Util/MessageBus.cs new file mode 100644 index 000000000..288f97227 --- /dev/null +++ b/ILSpy/Util/MessageBus.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Specialized; + +using TomsToolbox.Essentials; + +namespace ICSharpCode.ILSpy.Util +{ + public static class MessageBus + { + public static void Send(object sender, T e) + where T : EventArgs + { + MessageBus.Send(sender, e); + } + } + + /// + /// Simple, minimalistic message bus. + /// + /// The type of the message event arguments + public static class MessageBus + where T : EventArgs + { + private static readonly WeakEventSource Subscriptions = new(); + + public static event EventHandler Subscribers { + add => Subscriptions.Subscribe(value); + remove => Subscriptions.Unsubscribe(value); + } + + public static void Send(object sender, T e) + { + Subscriptions.Raise(sender, e); + } + } + + public abstract class WrappedEventArgs : EventArgs + { + private readonly T inner; + + protected WrappedEventArgs(T inner) + { + this.inner = inner; + } + + public static implicit operator T(WrappedEventArgs outer) + { + return outer.inner; + } + } + + public class CurrentAssemblyListChangedEventArgs(NotifyCollectionChangedEventArgs e) : WrappedEventArgs(e); + + public class DockWorkspaceActiveTabPageChangedEventArgs : EventArgs; +} \ No newline at end of file diff --git a/ILSpy/Views/DebugSteps.xaml.cs b/ILSpy/Views/DebugSteps.xaml.cs index 2c3e2e2e2..3b59c383f 100644 --- a/ILSpy/Views/DebugSteps.xaml.cs +++ b/ILSpy/Views/DebugSteps.xaml.cs @@ -8,6 +8,7 @@ using System.Windows.Input; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.ILSpy.Docking; +using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpy.ViewModels; using TomsToolbox.Wpf.Composition.Mef; @@ -35,7 +36,7 @@ namespace ICSharpCode.ILSpy InitializeComponent(); #if DEBUG - DockWorkspace.Instance.PropertyChanged += DockWorkspace_PropertyChanged; + MessageBus.Subscribers += DockWorkspace_ActiveTabPageChanged; filterSettings = DockWorkspace.Instance.ActiveTabPage.FilterSettings; filterSettings.PropertyChanged += FilterSettings_PropertyChanged; MainWindow.Instance.SelectionChanged += SelectionChanged; @@ -50,16 +51,11 @@ namespace ICSharpCode.ILSpy #endif } - private void DockWorkspace_PropertyChanged(object sender, PropertyChangedEventArgs e) + private void DockWorkspace_ActiveTabPageChanged(object sender, EventArgs e) { - switch (e.PropertyName) - { - case nameof(DockWorkspace.Instance.ActiveTabPage): - filterSettings.PropertyChanged -= FilterSettings_PropertyChanged; - filterSettings = DockWorkspace.Instance.ActiveTabPage.FilterSettings; - filterSettings.PropertyChanged += FilterSettings_PropertyChanged; - break; - } + filterSettings.PropertyChanged -= FilterSettings_PropertyChanged; + filterSettings = DockWorkspace.Instance.ActiveTabPage.FilterSettings; + filterSettings.PropertyChanged += FilterSettings_PropertyChanged; } private void WritingOptions_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) From 97bd5cb122674967e59982bac8440e288bb97903 Mon Sep 17 00:00:00 2001 From: tom-englert Date: Tue, 20 Aug 2024 10:53:52 +0200 Subject: [PATCH 24/35] Decouple Settings from MainWindow --- ILSpy/Analyzers/AnalyzerTreeView.cs | 15 +--- ILSpy/Commands/SetThemeCommand.cs | 6 +- ...{FilterSettings.cs => LanguageSettings.cs} | 60 +++++---------- ILSpy/MainWindow.xaml | 16 ++-- ILSpy/MainWindow.xaml.cs | 77 ++++++++----------- ILSpy/Options/DisplaySettingsPanel.xaml.cs | 7 +- ILSpy/Search/SearchPane.cs | 13 ++-- ILSpy/SessionSettings.cs | 18 +++-- ILSpy/TreeNodes/AssemblyTreeNode.cs | 2 +- ILSpy/TreeNodes/DerivedTypesEntryNode.cs | 2 +- ILSpy/TreeNodes/EventTreeNode.cs | 2 +- ILSpy/TreeNodes/FieldTreeNode.cs | 2 +- ILSpy/TreeNodes/ILSpyTreeNode.cs | 51 +++--------- ILSpy/TreeNodes/MethodTreeNode.cs | 2 +- ILSpy/TreeNodes/NamespaceTreeNode.cs | 2 +- ILSpy/TreeNodes/PropertyTreeNode.cs | 2 +- ILSpy/TreeNodes/ResourceListTreeNode.cs | 2 +- .../ResourceNodes/ResourceTreeNode.cs | 2 +- ILSpy/TreeNodes/ThreadingSupport.cs | 4 +- ILSpy/TreeNodes/TypeTreeNode.cs | 2 +- ILSpy/Util/MessageBus.cs | 5 ++ ILSpy/Util/SettingsService.cs | 20 +++++ .../ManageAssemblyListsViewModel.cs | 13 ++-- ILSpy/ViewModels/TabPageModel.cs | 26 +------ ILSpy/Views/DebugSteps.xaml.cs | 16 +--- 25 files changed, 145 insertions(+), 222 deletions(-) rename ILSpy/{FilterSettings.cs => LanguageSettings.cs} (80%) create mode 100644 ILSpy/Util/SettingsService.cs diff --git a/ILSpy/Analyzers/AnalyzerTreeView.cs b/ILSpy/Analyzers/AnalyzerTreeView.cs index 132f7c1db..945d62fcc 100644 --- a/ILSpy/Analyzers/AnalyzerTreeView.cs +++ b/ILSpy/Analyzers/AnalyzerTreeView.cs @@ -44,29 +44,22 @@ namespace ICSharpCode.ILSpy.Analyzers [Export] public class AnalyzerTreeView : SharpTreeView { - FilterSettings filterSettings; - public AnalyzerTreeView() { this.ShowRoot = false; this.BorderThickness = new Thickness(0); ContextMenuProvider.Add(this); - MessageBus.Subscribers += (sender, e) => MainWindow_Instance_CurrentAssemblyListChanged(sender, e); + MessageBus.Subscribers += (sender, e) => CurrentAssemblyList_Changed(sender, e); MessageBus.Subscribers += DockWorkspace_ActiveTabPageChanged; - filterSettings = MainWindow.Instance.SessionSettings.FilterSettings; - filterSettings.PropertyChanged += FilterSettings_PropertyChanged; + MessageBus.Subscribers += (sender, e) => LanguageSettings_PropertyChanged(sender, e); } private void DockWorkspace_ActiveTabPageChanged(object sender, EventArgs e) { this.Root ??= new AnalyzerRootNode { Language = MainWindow.Instance.CurrentLanguage }; - - filterSettings.PropertyChanged -= FilterSettings_PropertyChanged; - filterSettings = DockWorkspace.Instance.ActiveTabPage.FilterSettings; - filterSettings.PropertyChanged += FilterSettings_PropertyChanged; } - private void FilterSettings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + private void LanguageSettings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { switch (e.PropertyName) { @@ -77,7 +70,7 @@ namespace ICSharpCode.ILSpy.Analyzers } } - void MainWindow_Instance_CurrentAssemblyListChanged(object sender, NotifyCollectionChangedEventArgs e) + void CurrentAssemblyList_Changed(object sender, NotifyCollectionChangedEventArgs e) { if (e.Action == NotifyCollectionChangedAction.Reset) { diff --git a/ILSpy/Commands/SetThemeCommand.cs b/ILSpy/Commands/SetThemeCommand.cs index 9083300ab..2d1a0b32a 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/FilterSettings.cs b/ILSpy/LanguageSettings.cs similarity index 80% rename from ILSpy/FilterSettings.cs rename to ILSpy/LanguageSettings.cs index 6adb3200d..a9a00fade 100644 --- a/ILSpy/FilterSettings.cs +++ b/ILSpy/LanguageSettings.cs @@ -23,6 +23,7 @@ using System.Linq; 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 @@ namespace ICSharpCode.ILSpy /// 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.LanguageVersion = Language.LanguageVersions.FirstOrDefault(v => v.Version == (string)element.Element("LanguageVersion")) + ?? Language.LanguageVersions.LastOrDefault(); } public XElement SaveAsXml() @@ -63,33 +58,6 @@ namespace ICSharpCode.ILSpy ); } - 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 @@ namespace ICSharpCode.ILSpy { 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/MainWindow.xaml b/ILSpy/MainWindow.xaml index 411ff87b0..32d7981eb 100644 --- a/ILSpy/MainWindow.xaml +++ b/ILSpy/MainWindow.xaml @@ -123,9 +123,9 @@ - - - + + + @@ -189,25 +189,25 @@ - + - + - + + SelectedItem="{Binding SessionSettings.LanguageSettings.Language}"/> + SelectedItem="{Binding SessionSettings.LanguageSettings.LanguageVersion, UpdateSourceTrigger=PropertyChanged}"/> diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index b40d950a7..0f6a4953d 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -73,9 +73,7 @@ namespace ICSharpCode.ILSpy { bool refreshInProgress, changingActiveTab; readonly NavigationHistory history = new NavigationHistory(); - ILSpySettings spySettingsForMainWindow_Loaded; - SessionSettings sessionSettings; - FilterSettings filterSettings; + AssemblyList assemblyList; AssemblyListTreeNode assemblyListTreeNode; @@ -85,10 +83,6 @@ namespace ICSharpCode.ILSpy get { return instance; } } - public SessionSettings SessionSettings { - get { return sessionSettings; } - } - internal AssemblyListManager AssemblyListManager { get; } public SharpTreeView AssemblyTreeView { @@ -116,9 +110,10 @@ namespace ICSharpCode.ILSpy public MainWindow() { instance = this; - var spySettings = ILSpySettings.Load(); - this.spySettingsForMainWindow_Loaded = spySettings; - this.sessionSettings = new SessionSettings(spySettings); + + var spySettings = SettingsService.Instance.SpySettings; + var sessionSettings = SettingsService.Instance.SessionSettings; + this.CurrentDecompilerSettings = DecompilerSettingsPanel.LoadDecompilerSettings(spySettings); this.CurrentDisplaySettings = DisplaySettingsPanel.LoadDisplaySettings(spySettings); this.AssemblyListManager = new AssemblyListManager(spySettings) { @@ -144,10 +139,11 @@ namespace ICSharpCode.ILSpy InitializeComponent(); InitToolPanes(); DockWorkspace.Instance.InitializeLayout(DockManager); - sessionSettings.PropertyChanged += SessionSettings_PropertyChanged; - filterSettings = sessionSettings.FilterSettings; - filterSettings.PropertyChanged += filterSettings_PropertyChanged; + + MessageBus.Subscribers += (sender, e) => SessionSettings_PropertyChanged(sender, e); + MessageBus.Subscribers += (sender, e) => filterSettings_PropertyChanged(sender, e); MessageBus.Subscribers += DockWorkspace_ActiveTabPageChanged; + InitMainMenu(); InitWindowMenu(); InitToolbar(); @@ -160,9 +156,6 @@ namespace ICSharpCode.ILSpy private void DockWorkspace_ActiveTabPageChanged(object sender, EventArgs e) { DockWorkspace dock = DockWorkspace.Instance; - filterSettings.PropertyChanged -= filterSettings_PropertyChanged; - filterSettings = dock.ActiveTabPage.FilterSettings; - filterSettings.PropertyChanged += filterSettings_PropertyChanged; var windowMenuItem = mainMenu.Items.OfType().First(m => (string)m.Tag == nameof(Properties.Resources._Window)); foreach (MenuItem menuItem in windowMenuItem.Items.OfType()) @@ -176,6 +169,8 @@ namespace ICSharpCode.ILSpy private void SessionSettings_PropertyChanged(object sender, PropertyChangedEventArgs e) { + var sessionSettings = SettingsService.Instance.SessionSettings; + switch (e.PropertyName) { case nameof(SessionSettings.ActiveAssemblyList): @@ -580,6 +575,8 @@ namespace ICSharpCode.ILSpy base.OnSourceInitialized(e); PresentationSource source = PresentationSource.FromVisual(this); + var sessionSettings = SettingsService.Instance.SessionSettings; + // Validate and Set Window Bounds Rect bounds = Rect.Transform(sessionSettings.WindowBounds, source.CompositionTarget.TransformToDevice); var boundsRect = new System.Drawing.Rectangle((int)bounds.Left, (int)bounds.Top, (int)bounds.Width, (int)bounds.Height); @@ -648,7 +645,7 @@ namespace ICSharpCode.ILSpy { LoadAssemblies(args.AssembliesToLoad, commandLineLoadedAssemblies, focusNode: false); if (args.Language != null) - filterSettings.Language = Languages.GetLanguage(args.Language); + SettingsService.Instance.SessionSettings.LanguageSettings.Language = Languages.GetLanguage(args.Language); return true; } @@ -658,6 +655,8 @@ namespace ICSharpCode.ILSpy /// void HandleCommandLineArgumentsAfterShowList(CommandLineArguments args, ILSpySettings spySettings = null) { + var sessionSettings = SettingsService.Instance.SessionSettings; + var relevantAssemblies = commandLineLoadedAssemblies.ToList(); commandLineLoadedAssemblies.Clear(); // clear references once we don't need them anymore NavigateOnLaunch(args.NavigateTo, sessionSettings.ActiveTreeViewPath, spySettings, relevantAssemblies); @@ -832,15 +831,13 @@ namespace ICSharpCode.ILSpy void MainWindow_Loaded(object sender, RoutedEventArgs e) { - DockWorkspace.Instance.TabPages.Add(new TabPageModel() { - FilterSettings = filterSettings.Clone() - }); + DockWorkspace.Instance.TabPages.Add(new() { }); DockWorkspace.Instance.ActiveTabPage = DockWorkspace.Instance.TabPages.First(); - ILSpySettings spySettings = this.spySettingsForMainWindow_Loaded; - this.spySettingsForMainWindow_Loaded = null; var loadPreviousAssemblies = Options.MiscSettingsPanel.CurrentMiscSettings.LoadPreviousAssemblies; + var sessionSettings = SettingsService.Instance.SessionSettings; + if (loadPreviousAssemblies) { // Load AssemblyList only in Loaded event so that WPF is initialized before we start the CPU-heavy stuff. @@ -870,7 +867,7 @@ namespace ICSharpCode.ILSpy this.assemblyList.Open(sessionSettings.ActiveAutoLoadedAssembly, true); } - Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => OpenAssemblies(spySettings))); + Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => OpenAssemblies(SettingsService.Instance.SpySettings))); } void OpenAssemblies(ILSpySettings spySettings) @@ -1000,7 +997,6 @@ namespace ICSharpCode.ILSpy assemblyList.CollectionChanged += assemblyList_Assemblies_CollectionChanged; assemblyListTreeNode = new AssemblyListTreeNode(assemblyList); - assemblyListTreeNode.FilterSettings = filterSettings.Clone(); assemblyListTreeNode.Select = x => SelectNode(x, inNewTabPage: false); AssemblyTreeView.Root = assemblyListTreeNode; @@ -1064,13 +1060,7 @@ namespace ICSharpCode.ILSpy public void RefreshTreeViewFilter() { - // filterSettings 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 assign a new clone to the ILSpyTreeNodes whenever the main - // mutable instance changes. - FilterSettings filterSettings = DockWorkspace.Instance.ActiveTabPage.FilterSettings.Clone(); - if (assemblyListTreeNode != null) - assemblyListTreeNode.FilterSettings = filterSettings; - SearchPane.UpdateFilter(filterSettings); + SearchPane.UpdateFilter(); } internal AssemblyListTreeNode AssemblyListTreeNode { @@ -1097,10 +1087,7 @@ namespace ICSharpCode.ILSpy { if (inNewTabPage) { - DockWorkspace.Instance.TabPages.Add( - new TabPageModel() { - FilterSettings = filterSettings.Clone() - }); + DockWorkspace.Instance.TabPages.Add(new TabPageModel() { }); DockWorkspace.Instance.ActiveTabPage = DockWorkspace.Instance.TabPages.Last(); AssemblyTreeView.SelectedItem = null; } @@ -1143,10 +1130,7 @@ namespace ICSharpCode.ILSpy { if (inNewTabPage) { - DockWorkspace.Instance.TabPages.Add( - new TabPageModel() { - FilterSettings = filterSettings.Clone() - }); + DockWorkspace.Instance.TabPages.Add(new TabPageModel() { }); DockWorkspace.Instance.ActiveTabPage = DockWorkspace.Instance.TabPages.Last(); } @@ -1519,8 +1503,9 @@ namespace ICSharpCode.ILSpy } } - public Language CurrentLanguage => DockWorkspace.Instance.ActiveTabPage.FilterSettings.Language; - public LanguageVersion CurrentLanguageVersion => DockWorkspace.Instance.ActiveTabPage.FilterSettings.LanguageVersion; + public Language CurrentLanguage => SettingsService.Instance.SessionSettings.LanguageSettings.Language; + + public LanguageVersion CurrentLanguageVersion => SettingsService.Instance.SessionSettings.LanguageSettings.LanguageVersion; public event SelectionChangedEventHandler SelectionChanged; @@ -1590,10 +1575,7 @@ namespace ICSharpCode.ILSpy { if (inNewTabPage) { - DockWorkspace.Instance.TabPages.Add( - new TabPageModel() { - FilterSettings = filterSettings.Clone() - }); + DockWorkspace.Instance.TabPages.Add(new() { }); DockWorkspace.Instance.ActiveTabPage = DockWorkspace.Instance.TabPages.Last(); } @@ -1646,18 +1628,19 @@ namespace ICSharpCode.ILSpy base.OnStateChanged(e); // store window state in settings only if it's not minimized if (this.WindowState != System.Windows.WindowState.Minimized) - sessionSettings.WindowState = this.WindowState; + SettingsService.Instance.SessionSettings.WindowState = this.WindowState; } protected override void OnClosing(CancelEventArgs e) { base.OnClosing(e); + var sessionSettings = SettingsService.Instance.SessionSettings; + sessionSettings.ActiveAssemblyList = assemblyList.ListName; sessionSettings.ActiveTreeViewPath = GetPathForNode(AssemblyTreeView.SelectedItem as SharpTreeNode); sessionSettings.ActiveAutoLoadedAssembly = GetAutoLoadedAssemblyNode(AssemblyTreeView.SelectedItem as SharpTreeNode); sessionSettings.WindowBounds = this.RestoreBounds; sessionSettings.DockLayout.Serialize(new XmlLayoutSerializer(DockManager)); - sessionSettings.FilterSettings = DockWorkspace.Instance.ActiveTabPage.FilterSettings.Clone(); sessionSettings.Save(); } diff --git a/ILSpy/Options/DisplaySettingsPanel.xaml.cs b/ILSpy/Options/DisplaySettingsPanel.xaml.cs index 0319b327d..3e81eb7fe 100644 --- a/ILSpy/Options/DisplaySettingsPanel.xaml.cs +++ b/ILSpy/Options/DisplaySettingsPanel.xaml.cs @@ -27,6 +27,7 @@ using System.Windows.Media; using System.Windows.Threading; using System.Xml.Linq; +using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX.Settings; namespace ICSharpCode.ILSpy.Options @@ -123,7 +124,7 @@ namespace ICSharpCode.ILSpy.Options s.ShowRawOffsetsAndBytesBeforeInstruction = (bool?)e.Attribute("ShowRawOffsetsAndBytesBeforeInstruction") ?? false; s.StyleWindowTitleBar = (bool?)e.Attribute("StyleWindowTitleBar") ?? false; - s.Theme = MainWindow.Instance.SessionSettings.Theme; + s.Theme = SettingsService.Instance.SessionSettings.Theme; return s; } @@ -154,8 +155,8 @@ namespace ICSharpCode.ILSpy.Options section.SetAttributeValue("ShowRawOffsetsAndBytesBeforeInstruction", s.ShowRawOffsetsAndBytesBeforeInstruction); section.SetAttributeValue("StyleWindowTitleBar", s.StyleWindowTitleBar); - MainWindow.Instance.SessionSettings.Theme = s.Theme; - var sessionSettings = MainWindow.Instance.SessionSettings.ToXml(); + SettingsService.Instance.SessionSettings.Theme = s.Theme; + var sessionSettings = SettingsService.Instance.SessionSettings.ToXml(); MainWindow.Instance.CurrentDisplaySettings.CopyValues(s); diff --git a/ILSpy/Search/SearchPane.cs b/ILSpy/Search/SearchPane.cs index e35a4da73..f3bd6a550 100644 --- a/ILSpy/Search/SearchPane.cs +++ b/ILSpy/Search/SearchPane.cs @@ -50,6 +50,7 @@ namespace ICSharpCode.ILSpy.Search /// [DataTemplate(typeof(SearchPaneModel))] [PartCreationPolicy(CreationPolicy.Shared)] + [Export] public partial class SearchPane : UserControl { const int MAX_RESULTS = 1000; @@ -57,7 +58,6 @@ namespace ICSharpCode.ILSpy.Search RunningSearch currentSearch; bool runSearchOnNextShow; IComparer resultsComparer; - FilterSettings filterSettings; public static readonly DependencyProperty ResultsProperty = DependencyProperty.Register("Results", typeof(ObservableCollection), typeof(SearchPane), @@ -84,11 +84,10 @@ namespace ICSharpCode.ILSpy.Search ContextMenuProvider.Add(listBox); MessageBus.Subscribers += (sender, e) => MainWindow_Instance_CurrentAssemblyListChanged(sender, e); - filterSettings = MainWindow.Instance.SessionSettings.FilterSettings; CompositionTarget.Rendering += UpdateResults; // This starts empty search right away, so do at the end (we're still in ctor) - searchModeComboBox.SelectedIndex = (int)MainWindow.Instance.SessionSettings.SelectedSearchMode; + searchModeComboBox.SelectedIndex = (int)SettingsService.Instance.SessionSettings.SelectedSearchMode; } void MainWindow_Instance_CurrentAssemblyListChanged(object sender, NotifyCollectionChangedEventArgs e) @@ -104,10 +103,8 @@ namespace ICSharpCode.ILSpy.Search } } - internal void UpdateFilter(FilterSettings settings) + internal void UpdateFilter() { - this.filterSettings = settings; - if (IsVisible) { StartSearch(this.SearchTerm); @@ -155,7 +152,7 @@ namespace ICSharpCode.ILSpy.Search void SearchModeComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { - MainWindow.Instance.SessionSettings.SelectedSearchMode = (SearchMode)searchModeComboBox.SelectedIndex; + SettingsService.Instance.SessionSettings.SelectedSearchMode = (SearchMode)searchModeComboBox.SelectedIndex; StartSearch(this.SearchTerm); } @@ -258,7 +255,7 @@ namespace ICSharpCode.ILSpy.Search searchProgressBar.IsIndeterminate = true; startedSearch = new RunningSearch(await mainWindow.CurrentAssemblyList.GetAllAssemblies(), searchTerm, (SearchMode)searchModeComboBox.SelectedIndex, mainWindow.CurrentLanguage, - filterSettings.ShowApiLevel); + SettingsService.Instance.SessionSettings.LanguageSettings.ShowApiLevel); currentSearch = startedSearch; await startedSearch.Run(); diff --git a/ILSpy/SessionSettings.cs b/ILSpy/SessionSettings.cs index 4004513c5..ad58d121f 100644 --- a/ILSpy/SessionSettings.cs +++ b/ILSpy/SessionSettings.cs @@ -28,6 +28,7 @@ using System.Xml.Linq; using ICSharpCode.ILSpy.Docking; using ICSharpCode.ILSpy.Themes; +using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX.Search; using ICSharpCode.ILSpyX.Settings; @@ -47,7 +48,7 @@ namespace ICSharpCode.ILSpy if (filterSettings == null) filterSettings = new XElement("FilterSettings"); - this.FilterSettings = new FilterSettings(filterSettings); + this.LanguageSettings = new LanguageSettings(filterSettings); this.ActiveAssemblyList = (string)doc.Element("ActiveAssemblyList"); @@ -72,10 +73,15 @@ namespace ICSharpCode.ILSpy void OnPropertyChanged([CallerMemberName] string propertyName = null) { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + var args = new PropertyChangedEventArgs(propertyName); + + PropertyChanged?.Invoke(this, args); + + MessageBus.Send(this, new SessionSettingsChangedEventArgs(args)); } - public FilterSettings FilterSettings { get; internal set; } + public LanguageSettings LanguageSettings { get; } + public SearchMode SelectedSearchMode { get; set; } public string Theme { @@ -113,16 +119,16 @@ namespace ICSharpCode.ILSpy } } - public WindowState WindowState = WindowState.Normal; + public WindowState WindowState; public Rect WindowBounds; internal static Rect DefaultWindowBounds = new Rect(10, 10, 750, 550); - public DockLayoutSettings DockLayout { get; private set; } + public DockLayoutSettings DockLayout { get; } public XElement ToXml() { XElement doc = new XElement("SessionSettings"); - doc.Add(this.FilterSettings.SaveAsXml()); + doc.Add(this.LanguageSettings.SaveAsXml()); if (this.ActiveAssemblyList != null) { doc.Add(new XElement("ActiveAssemblyList", this.ActiveAssemblyList)); diff --git a/ILSpy/TreeNodes/AssemblyTreeNode.cs b/ILSpy/TreeNodes/AssemblyTreeNode.cs index a92a98362..b28c0809f 100644 --- a/ILSpy/TreeNodes/AssemblyTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyTreeNode.cs @@ -449,7 +449,7 @@ namespace ICSharpCode.ILSpy.TreeNodes return dataObject; } - public override FilterResult Filter(FilterSettings settings) + public override FilterResult Filter(LanguageSettings settings) { if (settings.SearchTermMatches(LoadedAssembly.ShortName)) return FilterResult.Match; diff --git a/ILSpy/TreeNodes/DerivedTypesEntryNode.cs b/ILSpy/TreeNodes/DerivedTypesEntryNode.cs index 525bb8aa3..caea0a521 100644 --- a/ILSpy/TreeNodes/DerivedTypesEntryNode.cs +++ b/ILSpy/TreeNodes/DerivedTypesEntryNode.cs @@ -50,7 +50,7 @@ namespace ICSharpCode.ILSpy.TreeNodes public override object Icon => TypeTreeNode.GetIcon(type); - public override FilterResult Filter(FilterSettings settings) + public override FilterResult Filter(LanguageSettings settings) { if (settings.ShowApiLevel == ApiVisibility.PublicOnly && !IsPublicAPI) return FilterResult.Hidden; diff --git a/ILSpy/TreeNodes/EventTreeNode.cs b/ILSpy/TreeNodes/EventTreeNode.cs index 0499f5dff..30cd704f8 100644 --- a/ILSpy/TreeNodes/EventTreeNode.cs +++ b/ILSpy/TreeNodes/EventTreeNode.cs @@ -68,7 +68,7 @@ namespace ICSharpCode.ILSpy.TreeNodes return Images.GetIcon(MemberIcon.Event, MethodTreeNode.GetOverlayIcon(@event.Accessibility), @event.IsStatic); } - public override FilterResult Filter(FilterSettings settings) + public override FilterResult Filter(LanguageSettings settings) { if (settings.ShowApiLevel == ApiVisibility.PublicOnly && !IsPublicAPI) return FilterResult.Hidden; diff --git a/ILSpy/TreeNodes/FieldTreeNode.cs b/ILSpy/TreeNodes/FieldTreeNode.cs index 4df85b89f..4509f2ca6 100644 --- a/ILSpy/TreeNodes/FieldTreeNode.cs +++ b/ILSpy/TreeNodes/FieldTreeNode.cs @@ -69,7 +69,7 @@ namespace ICSharpCode.ILSpy.TreeNodes return Images.GetIcon(MemberIcon.Field, MethodTreeNode.GetOverlayIcon(field.Accessibility), field.IsStatic); } - public override FilterResult Filter(FilterSettings settings) + public override FilterResult Filter(LanguageSettings settings) { if (settings.ShowApiLevel == ApiVisibility.PublicOnly && !IsPublicAPI) return FilterResult.Hidden; diff --git a/ILSpy/TreeNodes/ILSpyTreeNode.cs b/ILSpy/TreeNodes/ILSpyTreeNode.cs index 56c055308..f5c2aa2cd 100644 --- a/ILSpy/TreeNodes/ILSpyTreeNode.cs +++ b/ILSpy/TreeNodes/ILSpyTreeNode.cs @@ -27,6 +27,7 @@ using System.Windows.Threading; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX.Abstractions; using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions; using ICSharpCode.ILSpyX.TreeView; @@ -38,25 +39,20 @@ namespace ICSharpCode.ILSpy.TreeNodes /// public abstract class ILSpyTreeNode : SharpTreeNode, ITreeNode { - FilterSettings filterSettings; bool childrenNeedFiltering; - public FilterSettings FilterSettings { - get { return filterSettings; } - set { - if (filterSettings != value) - { - filterSettings = value; - OnFilterSettingsChanged(); - } - } + public ILSpyTreeNode() + { + MessageBus.Subscribers += LanguageSettings_Changed; } + LanguageSettings LanguageSettings => SettingsService.Instance.SessionSettings.LanguageSettings; + public Language Language { - get { return filterSettings != null ? filterSettings.Language : Languages.AllLanguages[0]; } + get { return LanguageSettings.Language ?? Languages.AllLanguages[0]; } } - public virtual FilterResult Filter(FilterSettings settings) + public virtual FilterResult Filter(LanguageSettings settings) { if (string.IsNullOrEmpty(settings.SearchTerm)) return FilterResult.Match; @@ -92,7 +88,7 @@ namespace ICSharpCode.ILSpy.TreeNodes return false; } - internal protected override void OnChildrenChanged(NotifyCollectionChangedEventArgs e) + protected internal override void OnChildrenChanged(NotifyCollectionChangedEventArgs e) { if (e.NewItems != null) { @@ -111,27 +107,21 @@ namespace ICSharpCode.ILSpy.TreeNodes void ApplyFilterToChild(ILSpyTreeNode child) { - FilterResult r; - if (this.FilterSettings == null) - r = FilterResult.Match; - else - r = child.Filter(this.FilterSettings); + FilterResult r = child.Filter(this.LanguageSettings); + switch (r) { case FilterResult.Hidden: child.IsHidden = true; break; case FilterResult.Match: - child.FilterSettings = StripSearchTerm(this.FilterSettings); child.IsHidden = false; break; case FilterResult.Recurse: - child.FilterSettings = this.FilterSettings; child.EnsureChildrenFiltered(); child.IsHidden = child.Children.All(c => c.IsHidden); break; case FilterResult.MatchAndRecurse: - child.FilterSettings = StripSearchTerm(this.FilterSettings); child.EnsureChildrenFiltered(); child.IsHidden = child.Children.All(c => c.IsHidden); break; @@ -140,19 +130,7 @@ namespace ICSharpCode.ILSpy.TreeNodes } } - static FilterSettings StripSearchTerm(FilterSettings filterSettings) - { - if (filterSettings == null) - return null; - if (!string.IsNullOrEmpty(filterSettings.SearchTerm)) - { - filterSettings = filterSettings.Clone(); - filterSettings.SearchTerm = null; - } - return filterSettings; - } - - protected virtual void OnFilterSettingsChanged() + protected virtual void LanguageSettings_Changed(object sender, EventArgs e) { RaisePropertyChanged(nameof(Text)); if (IsVisible) @@ -166,11 +144,6 @@ namespace ICSharpCode.ILSpy.TreeNodes } } - /*protected override void OnIsVisibleChanged() - { - base.OnIsVisibleChanged(); - EnsureChildrenFiltered(); - }*/ internal void EnsureChildrenFiltered() { diff --git a/ILSpy/TreeNodes/MethodTreeNode.cs b/ILSpy/TreeNodes/MethodTreeNode.cs index c805d5a83..090055123 100644 --- a/ILSpy/TreeNodes/MethodTreeNode.cs +++ b/ILSpy/TreeNodes/MethodTreeNode.cs @@ -99,7 +99,7 @@ namespace ICSharpCode.ILSpy.TreeNodes language.DecompileMethod(MethodDefinition, output, options); } - public override FilterResult Filter(FilterSettings settings) + public override FilterResult Filter(LanguageSettings settings) { if (settings.ShowApiLevel == ApiVisibility.PublicOnly && !IsPublicAPI) return FilterResult.Hidden; diff --git a/ILSpy/TreeNodes/NamespaceTreeNode.cs b/ILSpy/TreeNodes/NamespaceTreeNode.cs index 19982834f..c3c759c70 100644 --- a/ILSpy/TreeNodes/NamespaceTreeNode.cs +++ b/ILSpy/TreeNodes/NamespaceTreeNode.cs @@ -57,7 +57,7 @@ namespace ICSharpCode.ILSpy.TreeNodes this.isPublicAPI = value; } - public override FilterResult Filter(FilterSettings settings) + public override FilterResult Filter(LanguageSettings settings) { if (settings.SearchTermMatches(name)) return FilterResult.MatchAndRecurse; diff --git a/ILSpy/TreeNodes/PropertyTreeNode.cs b/ILSpy/TreeNodes/PropertyTreeNode.cs index ab437f8d4..8374d094f 100644 --- a/ILSpy/TreeNodes/PropertyTreeNode.cs +++ b/ILSpy/TreeNodes/PropertyTreeNode.cs @@ -71,7 +71,7 @@ namespace ICSharpCode.ILSpy.TreeNodes MethodTreeNode.GetOverlayIcon(property.Accessibility), property.IsStatic); } - public override FilterResult Filter(FilterSettings settings) + public override FilterResult Filter(LanguageSettings settings) { if (settings.ShowApiLevel == ApiVisibility.PublicOnly && !IsPublicAPI) return FilterResult.Hidden; diff --git a/ILSpy/TreeNodes/ResourceListTreeNode.cs b/ILSpy/TreeNodes/ResourceListTreeNode.cs index d812b7966..5f143db9a 100644 --- a/ILSpy/TreeNodes/ResourceListTreeNode.cs +++ b/ILSpy/TreeNodes/ResourceListTreeNode.cs @@ -51,7 +51,7 @@ namespace ICSharpCode.ILSpy.TreeNodes this.Children.Add(ResourceTreeNode.Create(r)); } - public override FilterResult Filter(FilterSettings settings) + public override FilterResult Filter(LanguageSettings settings) { if (string.IsNullOrEmpty(settings.SearchTerm)) return FilterResult.MatchAndRecurse; diff --git a/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs b/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs index bb306aa9a..69502149f 100644 --- a/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs @@ -55,7 +55,7 @@ namespace ICSharpCode.ILSpy.TreeNodes public override object Icon => Images.Resource; - public override FilterResult Filter(FilterSettings settings) + public override FilterResult Filter(LanguageSettings settings) { if (settings.ShowApiLevel == ApiVisibility.PublicOnly && (Resource.Attributes & ManifestResourceAttributes.VisibilityMask) == ManifestResourceAttributes.Private) return FilterResult.Hidden; diff --git a/ILSpy/TreeNodes/ThreadingSupport.cs b/ILSpy/TreeNodes/ThreadingSupport.cs index 7f99b3afb..ce1d87515 100644 --- a/ILSpy/TreeNodes/ThreadingSupport.cs +++ b/ILSpy/TreeNodes/ThreadingSupport.cs @@ -138,7 +138,7 @@ namespace ICSharpCode.ILSpy.TreeNodes get { return Resources.Loading; } } - public override FilterResult Filter(FilterSettings settings) + public override FilterResult Filter(LanguageSettings settings) { return FilterResult.Match; } @@ -161,7 +161,7 @@ namespace ICSharpCode.ILSpy.TreeNodes this.text = text; } - public override FilterResult Filter(FilterSettings settings) + public override FilterResult Filter(LanguageSettings settings) { return FilterResult.Match; } diff --git a/ILSpy/TreeNodes/TypeTreeNode.cs b/ILSpy/TreeNodes/TypeTreeNode.cs index 5917f35e0..91f7e11c3 100644 --- a/ILSpy/TreeNodes/TypeTreeNode.cs +++ b/ILSpy/TreeNodes/TypeTreeNode.cs @@ -67,7 +67,7 @@ namespace ICSharpCode.ILSpy.TreeNodes } } - public override FilterResult Filter(FilterSettings settings) + public override FilterResult Filter(LanguageSettings settings) { if (settings.ShowApiLevel == ApiVisibility.PublicOnly && !IsPublicAPI) return FilterResult.Hidden; diff --git a/ILSpy/Util/MessageBus.cs b/ILSpy/Util/MessageBus.cs index 288f97227..7573cb33c 100644 --- a/ILSpy/Util/MessageBus.cs +++ b/ILSpy/Util/MessageBus.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Specialized; +using System.ComponentModel; using TomsToolbox.Essentials; @@ -51,5 +52,9 @@ namespace ICSharpCode.ILSpy.Util public class CurrentAssemblyListChangedEventArgs(NotifyCollectionChangedEventArgs e) : WrappedEventArgs(e); + public class LanguageSettingsChangedEventArgs(PropertyChangedEventArgs e) : WrappedEventArgs(e); + + public class SessionSettingsChangedEventArgs(PropertyChangedEventArgs e) : WrappedEventArgs(e); + public class DockWorkspaceActiveTabPageChangedEventArgs : EventArgs; } \ No newline at end of file diff --git a/ILSpy/Util/SettingsService.cs b/ILSpy/Util/SettingsService.cs new file mode 100644 index 000000000..0f7a07e6a --- /dev/null +++ b/ILSpy/Util/SettingsService.cs @@ -0,0 +1,20 @@ +using ICSharpCode.ILSpyX.Settings; + +namespace ICSharpCode.ILSpy.Util +{ + internal class SettingsService + { + public static readonly SettingsService Instance = new SettingsService(); + + private SettingsService() + { + this.SpySettings = ILSpySettings.Load(); + + SessionSettings = new(this.SpySettings); + } + + public ILSpySettings SpySettings { get; } + + public SessionSettings SessionSettings { get; } + } +} diff --git a/ILSpy/ViewModels/ManageAssemblyListsViewModel.cs b/ILSpy/ViewModels/ManageAssemblyListsViewModel.cs index 57f6edb96..23959c2c3 100644 --- a/ILSpy/ViewModels/ManageAssemblyListsViewModel.cs +++ b/ILSpy/ViewModels/ManageAssemblyListsViewModel.cs @@ -27,6 +27,7 @@ using System.Windows.Input; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.ILSpy.Commands; using ICSharpCode.ILSpy.Properties; +using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX; namespace ICSharpCode.ILSpy.ViewModels @@ -174,7 +175,7 @@ namespace ICSharpCode.ILSpy.ViewModels return; manager.ClearAll(); manager.CreateDefaultAssemblyLists(); - MainWindow.Instance.SessionSettings.ActiveAssemblyList = manager.AssemblyLists[0]; + SettingsService.Instance.SessionSettings.ActiveAssemblyList = manager.AssemblyLists[0]; } private void ExecuteDelete() @@ -189,9 +190,9 @@ namespace ICSharpCode.ILSpy.ViewModels if (manager.AssemblyLists.Count > 0) { SelectedAssemblyList = manager.AssemblyLists[Math.Max(0, index - 1)]; - if (MainWindow.Instance.SessionSettings.ActiveAssemblyList == assemblyList) + if (SettingsService.Instance.SessionSettings.ActiveAssemblyList == assemblyList) { - MainWindow.Instance.SessionSettings.ActiveAssemblyList = SelectedAssemblyList; + SettingsService.Instance.SessionSettings.ActiveAssemblyList = SelectedAssemblyList; } } } @@ -232,9 +233,9 @@ namespace ICSharpCode.ILSpy.ViewModels string assemblyList = SelectedAssemblyList; SelectedAssemblyList = dlg.ListName; manager.RenameList(assemblyList, dlg.ListName); - if (MainWindow.Instance.SessionSettings.ActiveAssemblyList == assemblyList) + if (SettingsService.Instance.SessionSettings.ActiveAssemblyList == assemblyList) { - MainWindow.Instance.SessionSettings.ActiveAssemblyList = manager.AssemblyLists[manager.AssemblyLists.Count - 1]; + SettingsService.Instance.SessionSettings.ActiveAssemblyList = manager.AssemblyLists[manager.AssemblyLists.Count - 1]; } } } @@ -272,7 +273,7 @@ namespace ICSharpCode.ILSpy.ViewModels private void ExecuteSelectAssemblyList() { - MainWindow.Instance.SessionSettings.ActiveAssemblyList = SelectedAssemblyList; + SettingsService.Instance.SessionSettings.ActiveAssemblyList = SelectedAssemblyList; this.parent.Close(); } } diff --git a/ILSpy/ViewModels/TabPageModel.cs b/ILSpy/ViewModels/TabPageModel.cs index 836560c2c..71063243e 100644 --- a/ILSpy/ViewModels/TabPageModel.cs +++ b/ILSpy/ViewModels/TabPageModel.cs @@ -17,11 +17,10 @@ // DEALINGS IN THE SOFTWARE. using System; -using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using ICSharpCode.ILSpy.TextView; +using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX; namespace ICSharpCode.ILSpy.ViewModels @@ -33,29 +32,6 @@ namespace ICSharpCode.ILSpy.ViewModels this.Title = Properties.Resources.NewTab; } - private FilterSettings filterSettings; - - public FilterSettings FilterSettings { - get => filterSettings; - set { - if (filterSettings != value) - { - filterSettings = value; - RaisePropertyChanged(nameof(FilterSettings)); - } - } - } - - public Language Language { - get => filterSettings.Language; - set => filterSettings.Language = value; - } - - public LanguageVersion LanguageVersion { - get => filterSettings.LanguageVersion; - set => filterSettings.LanguageVersion = value; - } - private bool supportsLanguageSwitching = true; public bool SupportsLanguageSwitching { diff --git a/ILSpy/Views/DebugSteps.xaml.cs b/ILSpy/Views/DebugSteps.xaml.cs index 3b59c383f..29aa4261b 100644 --- a/ILSpy/Views/DebugSteps.xaml.cs +++ b/ILSpy/Views/DebugSteps.xaml.cs @@ -29,16 +29,13 @@ namespace ICSharpCode.ILSpy #if DEBUG ILAstLanguage language; #endif - FilterSettings filterSettings; - public DebugSteps() { InitializeComponent(); #if DEBUG - MessageBus.Subscribers += DockWorkspace_ActiveTabPageChanged; - filterSettings = DockWorkspace.Instance.ActiveTabPage.FilterSettings; - filterSettings.PropertyChanged += FilterSettings_PropertyChanged; + MessageBus.Subscribers += (sender, e) => LanguageSettings_PropertyChanged(sender, e); + MainWindow.Instance.SelectionChanged += SelectionChanged; writingOptions.PropertyChanged += WritingOptions_PropertyChanged; @@ -51,13 +48,6 @@ namespace ICSharpCode.ILSpy #endif } - private void DockWorkspace_ActiveTabPageChanged(object sender, EventArgs e) - { - filterSettings.PropertyChanged -= FilterSettings_PropertyChanged; - filterSettings = DockWorkspace.Instance.ActiveTabPage.FilterSettings; - filterSettings.PropertyChanged += FilterSettings_PropertyChanged; - } - private void WritingOptions_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { DecompileAsync(lastSelectedStep); @@ -71,7 +61,7 @@ namespace ICSharpCode.ILSpy }); } - private void FilterSettings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + private void LanguageSettings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { #if DEBUG if (e.PropertyName == "Language") From d71394c43ab770c661230e0e18724ef05920a41d Mon Sep 17 00:00:00 2001 From: tom-englert Date: Tue, 20 Aug 2024 10:55:29 +0200 Subject: [PATCH 25/35] Simplify adding tabs --- BuildTools/format.bat | 4 +++- ILSpy/Docking/DockWorkspace.cs | 5 +++++ ILSpy/Docking/PaneCollection.cs | 8 ++++---- ILSpy/MainWindow.xaml.cs | 12 ++++-------- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/BuildTools/format.bat b/BuildTools/format.bat index be49dc68c..74018f350 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/ILSpy/Docking/DockWorkspace.cs b/ILSpy/Docking/DockWorkspace.cs index a1fcd68cd..0a4c8fe71 100644 --- a/ILSpy/Docking/DockWorkspace.cs +++ b/ILSpy/Docking/DockWorkspace.cs @@ -88,6 +88,11 @@ namespace ICSharpCode.ILSpy.Docking 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) { diff --git a/ILSpy/Docking/PaneCollection.cs b/ILSpy/Docking/PaneCollection.cs index 5b5861062..72b1b851b 100644 --- a/ILSpy/Docking/PaneCollection.cs +++ b/ILSpy/Docking/PaneCollection.cs @@ -20,15 +20,13 @@ using System.Collections; 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 @@ namespace ICSharpCode.ILSpy.Docking 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/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index 0f6a4953d..bebfd9862 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -831,8 +831,7 @@ namespace ICSharpCode.ILSpy void MainWindow_Loaded(object sender, RoutedEventArgs e) { - DockWorkspace.Instance.TabPages.Add(new() { }); - DockWorkspace.Instance.ActiveTabPage = DockWorkspace.Instance.TabPages.First(); + DockWorkspace.Instance.TabPages.Add(); var loadPreviousAssemblies = Options.MiscSettingsPanel.CurrentMiscSettings.LoadPreviousAssemblies; @@ -1087,8 +1086,7 @@ namespace ICSharpCode.ILSpy { if (inNewTabPage) { - DockWorkspace.Instance.TabPages.Add(new TabPageModel() { }); - DockWorkspace.Instance.ActiveTabPage = DockWorkspace.Instance.TabPages.Last(); + DockWorkspace.Instance.TabPages.Add(); AssemblyTreeView.SelectedItem = null; } @@ -1130,8 +1128,7 @@ namespace ICSharpCode.ILSpy { if (inNewTabPage) { - DockWorkspace.Instance.TabPages.Add(new TabPageModel() { }); - DockWorkspace.Instance.ActiveTabPage = DockWorkspace.Instance.TabPages.Last(); + DockWorkspace.Instance.TabPages.Add(); } // Ensure nodes exist @@ -1575,8 +1572,7 @@ namespace ICSharpCode.ILSpy { if (inNewTabPage) { - DockWorkspace.Instance.TabPages.Add(new() { }); - DockWorkspace.Instance.ActiveTabPage = DockWorkspace.Instance.TabPages.Last(); + DockWorkspace.Instance.TabPages.Add(); } if (e.Uri.Host == "aboutpage") From 25bbca76aaafb4323aa31ccc7155ed0004a0b538 Mon Sep 17 00:00:00 2001 From: tom-englert Date: Tue, 20 Aug 2024 10:55:58 +0200 Subject: [PATCH 26/35] Remove redundant language settings related code --- .../Builtin/TypeExposedByAnalyzer.cs | 8 ++--- ILSpy/Analyzers/AnalyzerSearchTreeNode.cs | 24 ++++----------- ILSpy/Analyzers/AnalyzerTreeNode.cs | 29 ++----------------- ILSpy/Analyzers/AnalyzerTreeView.cs | 20 ++----------- ILSpy/App.xaml.cs | 1 - ILSpy/Commands/DecompileAllCommand.cs | 3 +- ILSpy/Commands/SaveCodeContextMenuEntry.cs | 4 ++- ILSpy/Controls/TreeView/SharpTreeView.cs | 12 ++++---- ILSpy/LanguageSettings.cs | 2 +- ILSpy/Languages/Languages.cs | 24 +++++---------- ILSpy/TextView/DecompilerTextView.cs | 7 +++-- ILSpy/TreeNodes/ILSpyTreeNode.cs | 4 +-- ILSpy/Views/DebugSteps.xaml.cs | 4 +-- 13 files changed, 40 insertions(+), 102 deletions(-) diff --git a/ICSharpCode.ILSpyX/Analyzers/Builtin/TypeExposedByAnalyzer.cs b/ICSharpCode.ILSpyX/Analyzers/Builtin/TypeExposedByAnalyzer.cs index bfb240bf1..3a2acc8a1 100644 --- a/ICSharpCode.ILSpyX/Analyzers/Builtin/TypeExposedByAnalyzer.cs +++ b/ICSharpCode.ILSpyX/Analyzers/Builtin/TypeExposedByAnalyzer.cs @@ -17,15 +17,13 @@ // 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 System.ComponentModel.Composition; - - using ICSharpCode.Decompiler.TypeSystem; - /// /// Finds all entities that expose a type. /// @@ -146,4 +144,4 @@ namespace ICSharpCode.ILSpyX.Analyzers.Builtin return visitor.Found; } } -} +} \ No newline at end of file diff --git a/ILSpy/Analyzers/AnalyzerSearchTreeNode.cs b/ILSpy/Analyzers/AnalyzerSearchTreeNode.cs index 490bf5ec2..0a7bbb95c 100644 --- a/ILSpy/Analyzers/AnalyzerSearchTreeNode.cs +++ b/ILSpy/Analyzers/AnalyzerSearchTreeNode.cs @@ -86,29 +86,17 @@ namespace ICSharpCode.ILSpy.Analyzers 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 e2946c3e0..8972926a7 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 @@ namespace ICSharpCode.ILSpy.Analyzers 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 index 945d62fcc..5f53e9906 100644 --- a/ILSpy/Analyzers/AnalyzerTreeView.cs +++ b/ILSpy/Analyzers/AnalyzerTreeView.cs @@ -47,28 +47,12 @@ namespace ICSharpCode.ILSpy.Analyzers public AnalyzerTreeView() { this.ShowRoot = false; - this.BorderThickness = new Thickness(0); + this.BorderThickness = new(); + this.Root = new AnalyzerRootNode(); ContextMenuProvider.Add(this); MessageBus.Subscribers += (sender, e) => CurrentAssemblyList_Changed(sender, e); - MessageBus.Subscribers += DockWorkspace_ActiveTabPageChanged; - MessageBus.Subscribers += (sender, e) => LanguageSettings_PropertyChanged(sender, e); } - private void DockWorkspace_ActiveTabPageChanged(object sender, EventArgs e) - { - this.Root ??= new AnalyzerRootNode { Language = MainWindow.Instance.CurrentLanguage }; - } - - private void LanguageSettings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) - { - switch (e.PropertyName) - { - case "Language": - case "LanguageVersion": - ((AnalyzerRootNode)this.Root).Language = MainWindow.Instance.CurrentLanguage; - break; - } - } void CurrentAssemblyList_Changed(object sender, NotifyCollectionChangedEventArgs e) { diff --git a/ILSpy/App.xaml.cs b/ILSpy/App.xaml.cs index fc74c786d..d0b08f0f5 100644 --- a/ILSpy/App.xaml.cs +++ b/ILSpy/App.xaml.cs @@ -98,7 +98,6 @@ namespace ICSharpCode.ILSpy // Add data templates registered via MEF. Resources.MergedDictionaries.Add(DataTemplateManager.CreateDynamicDataTemplates(ExportProvider)); - Languages.Initialize(ExportProvider); EventManager.RegisterClassHandler(typeof(Window), Hyperlink.RequestNavigateEvent, new RequestNavigateEventHandler(Window_RequestNavigate)); diff --git a/ILSpy/Commands/DecompileAllCommand.cs b/ILSpy/Commands/DecompileAllCommand.cs index b6ccb0bd6..c905c9a87 100644 --- a/ILSpy/Commands/DecompileAllCommand.cs +++ b/ILSpy/Commands/DecompileAllCommand.cs @@ -28,6 +28,7 @@ using System.Threading.Tasks; using ICSharpCode.Decompiler; using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.TextView; +using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX; using TomsToolbox.Essentials; @@ -94,7 +95,7 @@ namespace ICSharpCode.ILSpy 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/SaveCodeContextMenuEntry.cs b/ILSpy/Commands/SaveCodeContextMenuEntry.cs index 897e6f12c..d7e38ada3 100644 --- a/ILSpy/Commands/SaveCodeContextMenuEntry.cs +++ b/ILSpy/Commands/SaveCodeContextMenuEntry.cs @@ -32,6 +32,8 @@ 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")] @@ -60,7 +62,7 @@ namespace ICSharpCode.ILSpy.TextView 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/Controls/TreeView/SharpTreeView.cs b/ILSpy/Controls/TreeView/SharpTreeView.cs index 3d4e3e7e6..faa7cb2f0 100644 --- a/ILSpy/Controls/TreeView/SharpTreeView.cs +++ b/ILSpy/Controls/TreeView/SharpTreeView.cs @@ -54,7 +54,7 @@ namespace ICSharpCode.ILSpy.Controls.TreeView RegisterCommands(); } - public static ResourceKey DefaultItemContainerStyleKey { get; private set; } + public static ResourceKey DefaultItemContainerStyleKey { get; } public SharpTreeView() { @@ -62,7 +62,7 @@ namespace ICSharpCode.ILSpy.Controls.TreeView } 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 @@ namespace ICSharpCode.ILSpy.Controls.TreeView } 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 @@ namespace ICSharpCode.ILSpy.Controls.TreeView } 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 @@ namespace ICSharpCode.ILSpy.Controls.TreeView } 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 @@ namespace ICSharpCode.ILSpy.Controls.TreeView } 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/LanguageSettings.cs b/ILSpy/LanguageSettings.cs index a9a00fade..ef7613ccf 100644 --- a/ILSpy/LanguageSettings.cs +++ b/ILSpy/LanguageSettings.cs @@ -43,7 +43,7 @@ namespace ICSharpCode.ILSpy public LanguageSettings(XElement element) { this.ShowApiLevel = (ApiVisibility?)(int?)element.Element("ShowAPILevel") ?? ApiVisibility.PublicAndInternal; - this.Language = Languages.GetLanguage((string)element.Element("Language")); + 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(); } diff --git a/ILSpy/Languages/Languages.cs b/ILSpy/Languages/Languages.cs index cbce690b8..a58df78da 100644 --- a/ILSpy/Languages/Languages.cs +++ b/ILSpy/Languages/Languages.cs @@ -16,41 +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(IExportProvider 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/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 4c53e6c18..5d7eb069e 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -56,6 +56,7 @@ using ICSharpCode.ILSpy.AvalonEdit; using ICSharpCode.ILSpy.Options; using ICSharpCode.ILSpy.Themes; using ICSharpCode.ILSpy.TreeNodes; +using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpy.ViewModels; using ICSharpCode.ILSpyX; @@ -392,7 +393,7 @@ namespace ICSharpCode.ILSpy.TextView if (segment.Reference is ICSharpCode.Decompiler.Disassembler.OpCodeInfo code) { XmlDocumentationProvider docProvider = XmlDocLoader.MscorlibDocumentation; - DocumentationUIBuilder renderer = new DocumentationUIBuilder(new CSharpAmbience(), MainWindow.Instance.CurrentLanguage.SyntaxHighlighting); + DocumentationUIBuilder renderer = new DocumentationUIBuilder(new CSharpAmbience(), SettingsService.Instance.SessionSettings.LanguageSettings.Language.SyntaxHighlighting); renderer.AddSignatureBlock($"{code.Name} (0x{code.Code:x})"); if (docProvider != null) { @@ -442,7 +443,7 @@ namespace ICSharpCode.ILSpy.TextView static FlowDocument? CreateTooltipForEntity(IEntity resolved) { - Language currentLanguage = MainWindow.Instance.CurrentLanguage; + Language currentLanguage = SettingsService.Instance.SessionSettings.LanguageSettings.Language; DocumentationUIBuilder renderer = new DocumentationUIBuilder(new CSharpAmbience(), currentLanguage.SyntaxHighlighting); RichText richText = currentLanguage.GetRichTextTooltip(resolved); if (richText == null) @@ -533,7 +534,7 @@ namespace ICSharpCode.ILSpy.TextView { if (MainWindow.Instance.CurrentDisplaySettings.HighlightMatchingBraces) { - var result = MainWindow.Instance.CurrentLanguage.BracketSearcher.SearchBracket(textEditor.Document, textEditor.CaretOffset); + var result = SettingsService.Instance.SessionSettings.LanguageSettings.Language.BracketSearcher.SearchBracket(textEditor.Document, textEditor.CaretOffset); bracketHighlightRenderer.SetHighlight(result); } else diff --git a/ILSpy/TreeNodes/ILSpyTreeNode.cs b/ILSpy/TreeNodes/ILSpyTreeNode.cs index f5c2aa2cd..d2a3c93dc 100644 --- a/ILSpy/TreeNodes/ILSpyTreeNode.cs +++ b/ILSpy/TreeNodes/ILSpyTreeNode.cs @@ -48,9 +48,7 @@ namespace ICSharpCode.ILSpy.TreeNodes LanguageSettings LanguageSettings => SettingsService.Instance.SessionSettings.LanguageSettings; - public Language Language { - get { return LanguageSettings.Language ?? Languages.AllLanguages[0]; } - } + public Language Language => LanguageSettings.Language; public virtual FilterResult Filter(LanguageSettings settings) { diff --git a/ILSpy/Views/DebugSteps.xaml.cs b/ILSpy/Views/DebugSteps.xaml.cs index 29aa4261b..c4177e3aa 100644 --- a/ILSpy/Views/DebugSteps.xaml.cs +++ b/ILSpy/Views/DebugSteps.xaml.cs @@ -39,7 +39,7 @@ namespace ICSharpCode.ILSpy MainWindow.Instance.SelectionChanged += SelectionChanged; writingOptions.PropertyChanged += WritingOptions_PropertyChanged; - if (MainWindow.Instance.CurrentLanguage is ILAstLanguage l) + if (SettingsService.Instance.SessionSettings.LanguageSettings.Language is ILAstLanguage l) { l.StepperUpdated += ILAstStepperUpdated; language = l; @@ -70,7 +70,7 @@ namespace ICSharpCode.ILSpy { language.StepperUpdated -= ILAstStepperUpdated; } - if (MainWindow.Instance.CurrentLanguage is ILAstLanguage l) + if (SettingsService.Instance.SessionSettings.LanguageSettings.Language is ILAstLanguage l) { l.StepperUpdated += ILAstStepperUpdated; language = l; From e2414441134daa9938c0e35e1c698bececd2fdc0 Mon Sep 17 00:00:00 2001 From: tom-englert Date: Tue, 20 Aug 2024 10:56:37 +0200 Subject: [PATCH 27/35] Refactor AnalyzerTreeView into View/ViewModel --- ILSpy/Analyzers/AnalyzeCommand.cs | 27 ++- ILSpy/Analyzers/AnalyzerRootNode.cs | 42 +++++ ILSpy/Analyzers/AnalyzerTreeView.cs | 158 ------------------ ILSpy/Analyzers/AnalyzerTreeView.xaml | 23 +++ .../AnalyzerTreeView.xaml.cs} | 36 ++-- ILSpy/Analyzers/AnalyzerTreeViewModel.cs | 130 ++++++++++++++ ILSpy/Docking/DockWorkspace.cs | 18 +- ILSpy/MainWindow.xaml | 2 +- ILSpy/MainWindow.xaml.cs | 3 +- ILSpy/ViewModels/PaneModel.cs | 25 ++- ILSpy/ViewModels/TabPageModel.cs | 4 +- 11 files changed, 250 insertions(+), 218 deletions(-) create mode 100644 ILSpy/Analyzers/AnalyzerRootNode.cs delete mode 100644 ILSpy/Analyzers/AnalyzerTreeView.cs create mode 100644 ILSpy/Analyzers/AnalyzerTreeView.xaml rename ILSpy/{ViewModels/AnalyzerPaneModel.cs => Analyzers/AnalyzerTreeView.xaml.cs} (61%) create mode 100644 ILSpy/Analyzers/AnalyzerTreeViewModel.cs diff --git a/ILSpy/Analyzers/AnalyzeCommand.cs b/ILSpy/Analyzers/AnalyzeCommand.cs index d6cf8a7e9..889e6afa0 100644 --- a/ILSpy/Analyzers/AnalyzeCommand.cs +++ b/ILSpy/Analyzers/AnalyzeCommand.cs @@ -27,9 +27,9 @@ namespace ICSharpCode.ILSpy.Analyzers { [ExportContextMenuEntry(Header = nameof(Resources.Analyze), Icon = "Images/Search", Category = nameof(Resources.Analyze), InputGestureText = "Ctrl+R", Order = 100)] [PartCreationPolicy(CreationPolicy.Shared)] - internal sealed class AnalyzeCommand : SimpleCommand, IContextMenuEntry + internal sealed class AnalyzeContextMenuCommand : IContextMenuEntry { - private static readonly AnalyzerTreeView AnalyzerTreeView = App.ExportProvider.GetExportedValue(); + private static readonly AnalyzerTreeViewModel AnalyzerTreeView = App.ExportProvider.GetExportedValue(); public bool IsVisible(TextViewContext context) { @@ -70,29 +70,22 @@ namespace ICSharpCode.ILSpy.Analyzers AnalyzerTreeView.Analyze(entity); } } + } + + internal sealed class AnalyzeCommand : SimpleCommand + { + private static readonly AnalyzerTreeViewModel AnalyzerTreeView = App.ExportProvider.GetExportedValue(); public override bool CanExecute(object parameter) { - return AnalyzerTreeView.IsKeyboardFocusWithin - ? AnalyzerTreeView.SelectedItems.OfType().All(n => n is IMemberTreeNode) - : MainWindow.Instance.SelectedNodes.All(n => n is IMemberTreeNode); + return MainWindow.Instance.SelectedNodes.All(n => n is IMemberTreeNode); } public override void Execute(object parameter) { - if (AnalyzerTreeView.IsKeyboardFocusWithin) + foreach (var node in MainWindow.Instance.SelectedNodes.OfType()) { - foreach (var node in AnalyzerTreeView.SelectedItems.OfType().ToArray()) - { - AnalyzerTreeView.Analyze(node.Member); - } - } - else - { - foreach (var node in MainWindow.Instance.SelectedNodes.OfType()) - { - 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 000000000..17709687f --- /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/AnalyzerTreeView.cs b/ILSpy/Analyzers/AnalyzerTreeView.cs deleted file mode 100644 index 5f53e9906..000000000 --- a/ILSpy/Analyzers/AnalyzerTreeView.cs +++ /dev/null @@ -1,158 +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.ComponentModel.Composition; -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.ILSpy.Util; -using ICSharpCode.ILSpyX.TreeView; - -using TomsToolbox.Wpf.Composition.Mef; - -namespace ICSharpCode.ILSpy.Analyzers -{ - /// - /// Analyzer tree view. - /// - [DataTemplate(typeof(AnalyzerPaneModel))] - [PartCreationPolicy(CreationPolicy.Shared)] - [Export] - public class AnalyzerTreeView : SharpTreeView - { - public AnalyzerTreeView() - { - this.ShowRoot = false; - this.BorderThickness = new(); - this.Root = new AnalyzerRootNode(); - ContextMenuProvider.Add(this); - MessageBus.Subscribers += (sender, e) => CurrentAssemblyList_Changed(sender, e); - } - - - void CurrentAssemblyList_Changed(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 000000000..28bba030c --- /dev/null +++ b/ILSpy/Analyzers/AnalyzerTreeView.xaml @@ -0,0 +1,23 @@ + + + + + + + \ No newline at end of file diff --git a/ILSpy/ViewModels/AnalyzerPaneModel.cs b/ILSpy/Analyzers/AnalyzerTreeView.xaml.cs similarity index 61% rename from ILSpy/ViewModels/AnalyzerPaneModel.cs rename to ILSpy/Analyzers/AnalyzerTreeView.xaml.cs index ca92e5dae..bff239a46 100644 --- a/ILSpy/ViewModels/AnalyzerPaneModel.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 @@ -17,22 +17,34 @@ // DEALINGS IN THE SOFTWARE. using System.ComponentModel.Composition; -using System.Windows.Input; +using System.Windows.Controls; -namespace ICSharpCode.ILSpy.ViewModels +using ICSharpCode.ILSpyX.TreeView; + +using TomsToolbox.Wpf.Composition.Mef; + +namespace ICSharpCode.ILSpy.Analyzers { - [ExportToolPane] - [PartCreationPolicy(CreationPolicy.Shared)] - public class AnalyzerPaneModel : ToolPaneModel + /// + /// Interaction logic for AnalyzerTreeView.xaml + /// + [DataTemplate(typeof(AnalyzerTreeViewModel))] + [PartCreationPolicy(CreationPolicy.NonShared)] + [Export] + public partial class AnalyzerTreeView { - public const string PaneContentId = "analyzerPane"; + public AnalyzerTreeView() + { + InitializeComponent(); + ContextMenuProvider.Add(this); + } - public AnalyzerPaneModel() + private void AnalyzerTreeView_OnSelectionChanged(object sender, SelectionChangedEventArgs e) { - ContentId = PaneContentId; - Title = Properties.Resources.Analyze; - ShortcutKey = new KeyGesture(Key.R, ModifierKeys.Control); - AssociatedCommand = ILSpyCommands.Analyze; + if (SelectedItem is SharpTreeNode sharpTreeNode) + { + FocusNode(sharpTreeNode); + } } } } diff --git a/ILSpy/Analyzers/AnalyzerTreeViewModel.cs b/ILSpy/Analyzers/AnalyzerTreeViewModel.cs new file mode 100644 index 000000000..fc7def755 --- /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/Docking/DockWorkspace.cs b/ILSpy/Docking/DockWorkspace.cs index 0a4c8fe71..6ae7a0bdc 100644 --- a/ILSpy/Docking/DockWorkspace.cs +++ b/ILSpy/Docking/DockWorkspace.cs @@ -35,6 +35,7 @@ using AvalonDock.Layout; using AvalonDock.Layout.Serialization; using ICSharpCode.AvalonEdit.Highlighting; +using ICSharpCode.ILSpy.Analyzers; using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpy.ViewModels; @@ -45,17 +46,17 @@ namespace ICSharpCode.ILSpy.Docking { public class DockWorkspace : ObservableObject, ILayoutUpdateStrategy { - private SessionSettings sessionSettings; + private static SessionSettings SessionSettings => SettingsService.Instance.SessionSettings; public static readonly DockWorkspace Instance = new(); private DockWorkspace() { this.TabPages.CollectionChanged += Documents_CollectionChanged; - MessageBus.Subscribers += (sender, e) => MainWindow_Instance_CurrentAssemblyListChanged(sender, e); + 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) { @@ -158,7 +159,7 @@ namespace ICSharpCode.ILSpy.Docking serializer.LayoutSerializationCallback += LayoutSerializationCallback; try { - sessionSettings.DockLayout.Deserialize(serializer); + SessionSettings.DockLayout.Deserialize(serializer); } finally { @@ -201,11 +202,6 @@ namespace ICSharpCode.ILSpy.Docking 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 @@ namespace ICSharpCode.ILSpy.Docking 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 @@ namespace ICSharpCode.ILSpy.Docking previousContainer.Children.Add(anchorableToShow); return true; case LegacyToolPaneLocation.Bottom: - previousContainer = GetContainer(); + previousContainer = GetContainer(); previousContainer.Children.Add(anchorableToShow); return true; default: diff --git a/ILSpy/MainWindow.xaml b/ILSpy/MainWindow.xaml index 32d7981eb..a6650e05a 100644 --- a/ILSpy/MainWindow.xaml +++ b/ILSpy/MainWindow.xaml @@ -35,7 +35,6 @@ BorderThickness="0" Visibility="Visible"> + diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index bebfd9862..810e70f99 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -135,7 +135,6 @@ namespace ICSharpCode.ILSpy { System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(sessionSettings.CurrentCulture); } - DockWorkspace.Instance.LoadSettings(sessionSettings); InitializeComponent(); InitToolPanes(); DockWorkspace.Instance.InitializeLayout(DockManager); @@ -352,7 +351,7 @@ namespace ICSharpCode.ILSpy private void InitToolPanes() { - var toolPanes = App.ExportProvider.GetExportedValues("ToolPane"); + var toolPanes = App.ExportProvider.GetExportedValues("ToolPane").OrderBy(item => item.Title); DockWorkspace.Instance.ToolPanes.AddRange(toolPanes); } diff --git a/ILSpy/ViewModels/PaneModel.cs b/ILSpy/ViewModels/PaneModel.cs index b214c9d97..c88bf869a 100644 --- a/ILSpy/ViewModels/PaneModel.cs +++ b/ILSpy/ViewModels/PaneModel.cs @@ -20,9 +20,11 @@ using System; using System.ComponentModel; using System.Windows.Input; +using TomsToolbox.Wpf; + namespace ICSharpCode.ILSpy.ViewModels { - public abstract class PaneModel : INotifyPropertyChanged + public abstract class PaneModel : ObservableObject { class CloseCommandImpl : ICommand { @@ -60,13 +62,6 @@ namespace ICSharpCode.ILSpy.ViewModels this.closeCommand = new CloseCommandImpl(this); } - public event PropertyChangedEventHandler PropertyChanged; - - protected void RaisePropertyChanged(string propertyName) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - private bool isSelected = false; public bool IsSelected { get => isSelected; @@ -74,7 +69,7 @@ namespace ICSharpCode.ILSpy.ViewModels if (isSelected != value) { isSelected = value; - RaisePropertyChanged(nameof(IsSelected)); + OnPropertyChanged(nameof(IsSelected)); } } } @@ -86,7 +81,7 @@ namespace ICSharpCode.ILSpy.ViewModels if (isActive != value) { isActive = value; - RaisePropertyChanged(nameof(IsActive)); + OnPropertyChanged(nameof(IsActive)); } } } @@ -98,7 +93,7 @@ namespace ICSharpCode.ILSpy.ViewModels if (isVisible != value) { isVisible = value; - RaisePropertyChanged(nameof(IsVisible)); + OnPropertyChanged(nameof(IsVisible)); } } } @@ -110,7 +105,7 @@ namespace ICSharpCode.ILSpy.ViewModels if (isCloseable != value) { isCloseable = value; - RaisePropertyChanged(nameof(IsCloseable)); + OnPropertyChanged(nameof(IsCloseable)); } } } @@ -122,7 +117,7 @@ namespace ICSharpCode.ILSpy.ViewModels if (closeCommand != value) { closeCommand = value; - RaisePropertyChanged(nameof(CloseCommand)); + OnPropertyChanged(nameof(CloseCommand)); } } } @@ -134,7 +129,7 @@ namespace ICSharpCode.ILSpy.ViewModels if (contentId != value) { contentId = value; - RaisePropertyChanged(nameof(ContentId)); + OnPropertyChanged(nameof(ContentId)); } } } @@ -146,7 +141,7 @@ namespace ICSharpCode.ILSpy.ViewModels if (title != value) { title = value; - RaisePropertyChanged(nameof(Title)); + OnPropertyChanged(nameof(Title)); } } } diff --git a/ILSpy/ViewModels/TabPageModel.cs b/ILSpy/ViewModels/TabPageModel.cs index 71063243e..10c28d47c 100644 --- a/ILSpy/ViewModels/TabPageModel.cs +++ b/ILSpy/ViewModels/TabPageModel.cs @@ -40,7 +40,7 @@ namespace ICSharpCode.ILSpy.ViewModels if (supportsLanguageSwitching != value) { supportsLanguageSwitching = value; - RaisePropertyChanged(nameof(SupportsLanguageSwitching)); + OnPropertyChanged(nameof(SupportsLanguageSwitching)); } } } @@ -53,7 +53,7 @@ namespace ICSharpCode.ILSpy.ViewModels if (content != value) { content = value; - RaisePropertyChanged(nameof(Content)); + OnPropertyChanged(nameof(Content)); } } } From 0330f38c0642c457b58e5aa44c8436cb8cebb2bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herrmannsd=C3=B6rfer=2C=20Felix?= Date: Wed, 21 Aug 2024 08:39:38 +0200 Subject: [PATCH 28/35] Include Subdirectories when checking for empty dotnet version directory --- ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs b/ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs index a5dd8c200..00a208674 100644 --- a/ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs +++ b/ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs @@ -253,9 +253,11 @@ namespace ICSharpCode.Decompiler.Metadata .Where(v => v.version != null); foreach (var folder in foundVersions.OrderBy(v => v.version)) { - if (folder.version >= version) - if(folder.directory.EnumerateFiles().Any()) - return folder.directory.Name; + if (folder.version >= version + && folder.directory.EnumerateFiles("*.dll", SearchOption.AllDirectories).Any()) + { + return folder.directory.Name; + } } return version.ToString(); } From 43f602211b00e67a7efd70c8ca3c0cbfb0ade4b1 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 24 Aug 2024 08:51:50 +0200 Subject: [PATCH 29/35] Fix #3159: CSharpAmbience.ConvertSymbol output incorrect for record structs and init properties. --- .../CSharp/OutputVisitor/CSharpAmbience.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs index 26474c8c8..beac7989c 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs @@ -85,6 +85,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor break; case ClassType.RecordStruct: writer.WriteKeyword(Roles.RecordKeyword, "record"); + writer.Space(); writer.WriteKeyword(Roles.StructKeyword, "struct"); break; default: @@ -181,8 +182,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor if ((ConversionFlags & ConversionFlags.ShowBody) == ConversionFlags.ShowBody && !(node is TypeDeclaration)) { - IProperty property = symbol as IProperty; - if (property != null) + if (symbol is IProperty property) { writer.Space(); writer.WriteToken(Roles.LBrace, "{"); @@ -195,7 +195,10 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } if (property.CanSet) { - writer.WriteKeyword(PropertyDeclaration.SetKeywordRole, "set"); + if ((ConversionFlags & ConversionFlags.SupportInitAccessors) != 0 && property.Setter.IsInitOnly) + writer.WriteKeyword(PropertyDeclaration.InitKeywordRole, "init"); + else + writer.WriteKeyword(PropertyDeclaration.SetKeywordRole, "set"); writer.WriteToken(Roles.Semicolon, ";"); writer.Space(); } From a4e372ad906c8506afde2d04473b08d971766c9c Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 24 Aug 2024 19:33:15 +0200 Subject: [PATCH 30/35] Fix naming convention rule for fields --- .editorconfig | 60 ++++++++++++++++----------------------------------- 1 file changed, 19 insertions(+), 41 deletions(-) diff --git a/.editorconfig b/.editorconfig index 64d038bd6..1ea262ade 100644 --- a/.editorconfig +++ b/.editorconfig @@ -121,49 +121,27 @@ 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 +# Naming styles: 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 + +# Symbol groups: +# all non-private fields +dotnet_naming_symbols.fields_symbols.applicable_accessibilities = * +dotnet_naming_symbols.fields_symbols.applicable_kinds = field +# all private/protected fields except constants +dotnet_naming_symbols.private_fields_symbols.applicable_accessibilities = private,protected,private_protected +dotnet_naming_symbols.private_fields_symbols.applicable_kinds = field + +# Naming rules: +# all non-private fields are pascal case +dotnet_naming_rule.fields_rule.severity = warning +dotnet_naming_rule.fields_rule.style = upper_camel_case_style +dotnet_naming_rule.fields_rule.symbols = fields_symbols +# all private fields are camel case +dotnet_naming_rule.private_fields_rule.severity = warning +dotnet_naming_rule.private_fields_rule.style = lower_camel_case_style +dotnet_naming_rule.private_fields_rule.symbols = private_fields_symbols # Errors and warnings From 3d34a20b48c8bb5daca70fbfacb5109a56e542e2 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 24 Aug 2024 22:15:48 +0200 Subject: [PATCH 31/35] Highlight severity of assembly resolve log messages to make it easier to see errors. --- .../ThemeAwareHighlightingColorizer.cs | 61 +---------------- ILSpy/Themes/ThemeManager.cs | 67 +++++++++++++++++++ ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs | 59 +++++++++++++--- ILSpy/TreeNodes/ModuleReferenceTreeNode.cs | 4 +- ILSpy/TreeNodes/ReferenceFolderTreeNode.cs | 16 ++--- 5 files changed, 128 insertions(+), 79 deletions(-) diff --git a/ILSpy/TextView/ThemeAwareHighlightingColorizer.cs b/ILSpy/TextView/ThemeAwareHighlightingColorizer.cs index 813909482..b92b944e6 100644 --- a/ILSpy/TextView/ThemeAwareHighlightingColorizer.cs +++ b/ILSpy/TextView/ThemeAwareHighlightingColorizer.cs @@ -1,6 +1,4 @@ -using System; using System.Collections.Generic; -using System.Windows.Media; using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.AvalonEdit.Rendering; @@ -40,66 +38,9 @@ public class ThemeAwareHighlightingColorizer : HighlightingColorizer if (!_darkColors.TryGetValue(lightColor, out var darkColor)) { - darkColor = lightColor.Clone(); - darkColor.Foreground = AdjustForDarkTheme(darkColor.Foreground); - darkColor.Background = AdjustForDarkTheme(darkColor.Background); - - _darkColors[lightColor] = darkColor; + _darkColors[lightColor] = darkColor = ThemeManager.GetColorForDarkTheme(lightColor); } return darkColor; } - - private static HighlightingBrush? AdjustForDarkTheme(HighlightingBrush? lightBrush) - { - if (lightBrush is SimpleHighlightingBrush simpleBrush && simpleBrush.GetBrush(null) is SolidColorBrush brush) - { - return new SimpleHighlightingBrush(AdjustForDarkTheme(brush.Color)); - } - - return lightBrush; - } - - private static Color AdjustForDarkTheme(Color color) - { - var c = System.Drawing.Color.FromArgb(color.R, color.G, color.B); - var (h, s, l) = (c.GetHue(), c.GetSaturation(), c.GetBrightness()); - - // Invert the lightness, but also increase it a bit - l = 1f - MathF.Pow(l, 1.2f); - - // Desaturate the colors, as they'd be too intense otherwise - if (s > 0.75f && l < 0.75f) - { - s *= 0.75f; - l *= 1.2f; - } - - var (r, g, b) = HslToRgb(h, s, l); - return Color.FromArgb(color.A, r, g, b); - } - - private static (byte r, byte g, byte b) HslToRgb(float h, float s, float l) - { - // https://en.wikipedia.org/wiki/HSL_and_HSV#HSL_to_RGB - - var c = (1f - Math.Abs(2f * l - 1f)) * s; - h = h % 360f / 60f; - var x = c * (1f - Math.Abs(h % 2f - 1f)); - - var (r1, g1, b1) = (int)Math.Floor(h) switch { - 0 => (c, x, 0f), - 1 => (x, c, 0f), - 2 => (0f, c, x), - 3 => (0f, x, c), - 4 => (x, 0f, c), - _ => (c, 0f, x) - }; - - var m = l - c / 2f; - var r = (byte)((r1 + m) * 255f); - var g = (byte)((g1 + m) * 255f); - var b = (byte)((b1 + m) * 255f); - return (r, g, b); - } } diff --git a/ILSpy/Themes/ThemeManager.cs b/ILSpy/Themes/ThemeManager.cs index d0210a284..419e4026c 100644 --- a/ILSpy/Themes/ThemeManager.cs +++ b/ILSpy/Themes/ThemeManager.cs @@ -132,5 +132,72 @@ namespace ICSharpCode.ILSpy.Themes } } } + + public static HighlightingColor GetColorForDarkTheme(HighlightingColor lightColor) + { + if (lightColor.Foreground is null && lightColor.Background is null) + { + return lightColor; + } + + var darkColor = lightColor.Clone(); + darkColor.Foreground = AdjustForDarkTheme(darkColor.Foreground); + darkColor.Background = AdjustForDarkTheme(darkColor.Background); + + return darkColor; + } + + private static HighlightingBrush? AdjustForDarkTheme(HighlightingBrush? lightBrush) + { + if (lightBrush is SimpleHighlightingBrush simpleBrush && simpleBrush.GetBrush(null) is SolidColorBrush brush) + { + return new SimpleHighlightingBrush(AdjustForDarkTheme(brush.Color)); + } + + return lightBrush; + } + + private static Color AdjustForDarkTheme(Color color) + { + var c = System.Drawing.Color.FromArgb(color.R, color.G, color.B); + var (h, s, l) = (c.GetHue(), c.GetSaturation(), c.GetBrightness()); + + // Invert the lightness, but also increase it a bit + l = 1f - MathF.Pow(l, 1.2f); + + // Desaturate the colors, as they'd be too intense otherwise + if (s > 0.75f && l < 0.75f) + { + s *= 0.75f; + l *= 1.2f; + } + + var (r, g, b) = HslToRgb(h, s, l); + return Color.FromArgb(color.A, r, g, b); + } + + private static (byte r, byte g, byte b) HslToRgb(float h, float s, float l) + { + // https://en.wikipedia.org/wiki/HSL_and_HSV#HSL_to_RGB + + var c = (1f - Math.Abs(2f * l - 1f)) * s; + h = h % 360f / 60f; + var x = c * (1f - Math.Abs(h % 2f - 1f)); + + var (r1, g1, b1) = (int)Math.Floor(h) switch { + 0 => (c, x, 0f), + 1 => (x, c, 0f), + 2 => (0f, c, x), + 3 => (0f, x, c), + 4 => (x, 0f, c), + _ => (c, 0f, x) + }; + + var m = l - c / 2f; + var r = (byte)((r1 + m) * 255f); + var g = (byte)((g1 + m) * 255f); + var b = (byte)((b1 + m) * 255f); + return (r, g, b); + } } } diff --git a/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs b/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs index 708eeb51d..f0046694b 100644 --- a/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs @@ -17,15 +17,17 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Windows; +using System.Windows.Media; using System.Windows.Threading; +using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.ILSpy.Themes; using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions; -using TomsToolbox.Wpf.Controls; - namespace ICSharpCode.ILSpy.TreeNodes { /// @@ -98,25 +100,66 @@ namespace ICSharpCode.ILSpy.TreeNodes var loaded = parentAssembly.LoadedAssembly.LoadedAssemblyReferencesInfo.TryGetInfo(r.FullName, out var info); if (r.IsWindowsRuntime) { - language.WriteCommentLine(output, r.FullName + " [WinRT]" + (!loaded ? " (unresolved)" : "")); + output.WriteLine(r.FullName + " [WinRT]" + (!loaded ? " (unresolved)" : "")); } else { - language.WriteCommentLine(output, r.FullName + (!loaded ? " (unresolved)" : "")); + output.WriteLine(r.FullName + (!loaded ? " (unresolved)" : "")); } if (loaded) { output.Indent(); - language.WriteCommentLine(output, "Assembly reference loading information:"); + output.WriteLine("Assembly reference loading information:"); if (info.HasErrors) - language.WriteCommentLine(output, "There were some problems during assembly reference load, see below for more information!"); - foreach (var item in info.Messages) { - language.WriteCommentLine(output, $"{item.Item1}: {item.Item2}"); + output.WriteLine("There were some problems during assembly reference load, see below for more information!"); } + PrintAssemblyLoadLogMessages(output, info); output.Unindent(); output.WriteLine(); } } + + internal static void PrintAssemblyLoadLogMessages(ITextOutput output, UnresolvedAssemblyNameReference asm) + { + HighlightingColor red = GetColor(Colors.Red); + HighlightingColor yellow = GetColor(Colors.Yellow); + + var smartOutput = output as ISmartTextOutput; + + foreach (var item in asm.Messages) + { + switch (item.Item1) + { + case MessageKind.Error: + smartOutput?.BeginSpan(red); + output.Write("Error: "); + smartOutput?.EndSpan(); + break; + case MessageKind.Warning: + smartOutput?.BeginSpan(yellow); + output.Write("Warning: "); + smartOutput?.EndSpan(); + break; + default: + output.Write(item.Item1 + ": "); + break; + } + output.WriteLine(item.Item2); + } + + static HighlightingColor GetColor(Color color) + { + var hc = new HighlightingColor { + Foreground = new SimpleHighlightingBrush(color), + FontWeight = FontWeights.Bold + }; + if (ThemeManager.Current.IsDarkTheme) + { + return ThemeManager.GetColorForDarkTheme(hc); + } + return hc; + } + } } } diff --git a/ILSpy/TreeNodes/ModuleReferenceTreeNode.cs b/ILSpy/TreeNodes/ModuleReferenceTreeNode.cs index b9a1d6de2..24fe4eb51 100644 --- a/ILSpy/TreeNodes/ModuleReferenceTreeNode.cs +++ b/ILSpy/TreeNodes/ModuleReferenceTreeNode.cs @@ -87,8 +87,8 @@ namespace ICSharpCode.ILSpy.TreeNodes public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { - language.WriteCommentLine(output, moduleName); - language.WriteCommentLine(output, containsMetadata ? "contains metadata" : "contains no metadata"); + output.WriteLine(moduleName); + output.WriteLine(containsMetadata ? "contains metadata" : "contains no metadata"); } } } diff --git a/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs b/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs index 2f3942083..355a96537 100644 --- a/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs +++ b/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs @@ -60,12 +60,12 @@ namespace ICSharpCode.ILSpy.TreeNodes { string targetFramework = parentAssembly.LoadedAssembly.GetTargetFrameworkIdAsync().GetAwaiter().GetResult(); string runtimePack = parentAssembly.LoadedAssembly.GetRuntimePackAsync().GetAwaiter().GetResult(); - language.WriteCommentLine(output, $"Detected TargetFramework-Id: {targetFramework}"); - language.WriteCommentLine(output, $"Detected RuntimePack: {runtimePack}"); + output.WriteLine($"Detected TargetFramework-Id: {targetFramework}"); + output.WriteLine($"Detected RuntimePack: {runtimePack}"); App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(EnsureLazyChildren)); output.WriteLine(); - language.WriteCommentLine(output, "Referenced assemblies (in metadata order):"); + output.WriteLine("Referenced assemblies (in metadata order):"); // Show metadata order of references foreach (var node in this.Children.OfType()) node.Decompile(language, output, options); @@ -73,16 +73,14 @@ namespace ICSharpCode.ILSpy.TreeNodes output.WriteLine(); output.WriteLine(); // Show full assembly load log: - language.WriteCommentLine(output, "Assembly load log including transitive references:"); + output.WriteLine("Assembly load log including transitive references:"); var info = parentAssembly.LoadedAssembly.LoadedAssemblyReferencesInfo; + foreach (var asm in info.Entries) { - language.WriteCommentLine(output, asm.FullName); + output.WriteLine(asm.FullName); output.Indent(); - foreach (var item in asm.Messages) - { - language.WriteCommentLine(output, $"{item.Item1}: {item.Item2}"); - } + AssemblyReferenceTreeNode.PrintAssemblyLoadLogMessages(output, asm); output.Unindent(); output.WriteLine(); } From 74f4758d893046e3718d9a745510fdea6b8772c2 Mon Sep 17 00:00:00 2001 From: tom-englert Date: Wed, 21 Aug 2024 14:55:02 +0200 Subject: [PATCH 32/35] Move more settings-related items from MainWindow to SettingsService --- ICSharpCode.ILSpyX/AssemblyListManager.cs | 1 + ILSpy.ReadyToRun/ReadyToRunLanguage.cs | 3 +- ILSpy/App.xaml.cs | 3 +- ILSpy/ExtensionMethods.cs | 33 +++++++++- ILSpy/Languages/CSharpILMixedLanguage.cs | 3 +- ILSpy/Languages/CSharpLanguage.cs | 7 ++- ILSpy/Languages/ILLanguage.cs | 3 +- ILSpy/MainWindow.xaml.cs | 63 +++---------------- ILSpy/Metadata/DebugMetadataTablesTreeNode.cs | 3 +- ILSpy/Metadata/MetadataTablesTreeNode.cs | 3 +- ILSpy/Options/DecompilerSettingsPanel.xaml.cs | 11 ++-- ILSpy/Options/DisplaySettingsPanel.xaml.cs | 10 +-- ILSpy/Search/SearchPane.cs | 4 +- ILSpy/TextView/DecompilerTextView.cs | 22 +++---- ILSpy/TextView/DocumentationUIBuilder.cs | 5 +- ILSpy/Themes/WindowStyleManagerBehavior.cs | 9 +-- ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs | 3 +- ILSpy/TreeNodes/AssemblyTreeNode.cs | 5 +- ILSpy/TreeNodes/ILSpyTreeNode.cs | 4 +- ILSpy/Util/SettingsService.cs | 26 ++++++-- .../ManageAssemblyListsViewModel.cs | 2 +- ILSpy/ViewModels/SearchPaneModel.cs | 2 +- ILSpy/Views/DebugSteps.xaml.cs | 2 +- 23 files changed, 121 insertions(+), 106 deletions(-) diff --git a/ICSharpCode.ILSpyX/AssemblyListManager.cs b/ICSharpCode.ILSpyX/AssemblyListManager.cs index 83f264f6d..8a886df5f 100644 --- a/ICSharpCode.ILSpyX/AssemblyListManager.cs +++ b/ICSharpCode.ILSpyX/AssemblyListManager.cs @@ -56,6 +56,7 @@ namespace ICSharpCode.ILSpyX } public bool ApplyWinRTProjections { get; set; } + public bool UseDebugSymbols { get; set; } public ObservableCollection AssemblyLists { get; } = new ObservableCollection(); diff --git a/ILSpy.ReadyToRun/ReadyToRunLanguage.cs b/ILSpy.ReadyToRun/ReadyToRunLanguage.cs index 410e49b67..b201a6608 100644 --- a/ILSpy.ReadyToRun/ReadyToRunLanguage.cs +++ b/ILSpy.ReadyToRun/ReadyToRunLanguage.cs @@ -33,6 +33,7 @@ using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Solution; using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX; using ILCompiler.Reflection.ReadyToRun; @@ -174,7 +175,7 @@ namespace ICSharpCode.ILSpy.ReadyToRun .GroupBy(m => m.MethodHandle) .ToDictionary(g => g.Key, g => g.ToArray()); } - var displaySettings = MainWindow.Instance.CurrentDisplaySettings; + var displaySettings = SettingsService.Instance.DisplaySettings; bool showMetadataTokens = displaySettings.ShowMetadataTokens; bool showMetadataTokensInBase10 = displaySettings.ShowMetadataTokensInBase10; #if STRESS diff --git a/ILSpy/App.xaml.cs b/ILSpy/App.xaml.cs index d0b08f0f5..c136891a1 100644 --- a/ILSpy/App.xaml.cs +++ b/ILSpy/App.xaml.cs @@ -201,7 +201,8 @@ namespace ICSharpCode.ILSpy protected override void OnStartup(StartupEventArgs e) { var output = new StringBuilder(); - if (ILSpy.MainWindow.FormatExceptions(StartupExceptions.ToArray(), output)) + + if (StartupExceptions.FormatExceptions(output)) { MessageBox.Show(output.ToString(), "Sorry we crashed!"); Environment.Exit(1); diff --git a/ILSpy/ExtensionMethods.cs b/ILSpy/ExtensionMethods.cs index 68c604a0d..374ad026e 100644 --- a/ILSpy/ExtensionMethods.cs +++ b/ILSpy/ExtensionMethods.cs @@ -20,12 +20,14 @@ using System; using System.Collections.Generic; +using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX; namespace ICSharpCode.ILSpy @@ -75,7 +77,7 @@ namespace ICSharpCode.ILSpy public static ICompilation? GetTypeSystemWithCurrentOptionsOrNull(this MetadataFile file) { return LoadedAssemblyExtensions.GetLoadedAssembly(file) - .GetTypeSystemOrNull(DecompilerTypeSystem.GetOptions(MainWindow.Instance.CurrentDecompilerSettings)); + .GetTypeSystemOrNull(DecompilerTypeSystem.GetOptions(SettingsService.Instance.DecompilerSettings)); } #region DPI independence @@ -162,5 +164,34 @@ namespace ICSharpCode.ILSpy { return color?.R * 0.3 + color?.G * 0.6 + color?.B * 0.1 ?? 0.0; } + + internal static bool FormatExceptions(this IList exceptions, StringBuilder output) + { + if (exceptions.Count == 0) + return false; + bool first = true; + + foreach (var item in exceptions) + { + if (first) + first = false; + else + output.AppendLine("-------------------------------------------------"); + output.AppendLine("Error(s) loading plugin: " + item.PluginName); + if (item.Exception is System.Reflection.ReflectionTypeLoadException) + { + var e = (System.Reflection.ReflectionTypeLoadException)item.Exception; + foreach (var ex in e.LoaderExceptions) + { + output.AppendLine(ex.ToString()); + output.AppendLine(); + } + } + else + output.AppendLine(item.Exception.ToString()); + } + + return true; + } } } diff --git a/ILSpy/Languages/CSharpILMixedLanguage.cs b/ILSpy/Languages/CSharpILMixedLanguage.cs index 31975cc24..4f7ca57cc 100644 --- a/ILSpy/Languages/CSharpILMixedLanguage.cs +++ b/ILSpy/Languages/CSharpILMixedLanguage.cs @@ -34,6 +34,7 @@ using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; +using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX; using ICSharpCode.ILSpyX.Extensions; @@ -49,7 +50,7 @@ namespace ICSharpCode.ILSpy protected override ReflectionDisassembler CreateDisassembler(ITextOutput output, DecompilationOptions options) { - var displaySettings = MainWindow.Instance.CurrentDisplaySettings; + var displaySettings = SettingsService.Instance.DisplaySettings; return new ReflectionDisassembler(output, new MixedMethodBodyDisassembler(output, options) { DetectControlStructure = detectControlStructure, diff --git a/ILSpy/Languages/CSharpLanguage.cs b/ILSpy/Languages/CSharpLanguage.cs index dcf7e91a2..df801511c 100644 --- a/ILSpy/Languages/CSharpLanguage.cs +++ b/ILSpy/Languages/CSharpLanguage.cs @@ -41,6 +41,7 @@ using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.TreeNodes; +using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX; using LanguageVersion = ICSharpCode.ILSpyX.LanguageVersion; @@ -572,7 +573,7 @@ namespace ICSharpCode.ILSpy CSharpAmbience ambience = new CSharpAmbience(); // Do not forget to update CSharpAmbienceTests.ILSpyMainTreeViewTypeFlags, if this ever changes. ambience.ConversionFlags = ConversionFlags.ShowTypeParameterList | ConversionFlags.PlaceReturnTypeAfterParameterList; - if (MainWindow.Instance.CurrentDecompilerSettings.LiftNullables) + if (SettingsService.Instance.DecompilerSettings.LiftNullables) { ambience.ConversionFlags |= ConversionFlags.UseNullableSpecifierForValueTypes; } @@ -775,7 +776,7 @@ namespace ICSharpCode.ILSpy public override bool ShowMember(IEntity member) { MetadataFile assembly = member.ParentModule.MetadataFile; - return showAllMembers || !CSharpDecompiler.MemberIsHidden(assembly, member.MetadataToken, MainWindow.Instance.CurrentDecompilerSettings); + return showAllMembers || !CSharpDecompiler.MemberIsHidden(assembly, member.MetadataToken, SettingsService.Instance.DecompilerSettings); } public override RichText GetRichTextTooltip(IEntity entity) @@ -784,7 +785,7 @@ namespace ICSharpCode.ILSpy var output = new StringWriter(); var decoratedWriter = new TextWriterTokenWriter(output); var writer = new CSharpHighlightingTokenWriter(TokenWriter.InsertRequiredSpaces(decoratedWriter), locatable: decoratedWriter); - var settings = MainWindow.Instance.CurrentDecompilerSettings; + var settings = SettingsService.Instance.DecompilerSettings; if (!settings.LiftNullables) { flags &= ~ConversionFlags.UseNullableSpecifierForValueTypes; diff --git a/ILSpy/Languages/ILLanguage.cs b/ILSpy/Languages/ILLanguage.cs index ab8f89ab7..22d060b37 100644 --- a/ILSpy/Languages/ILLanguage.cs +++ b/ILSpy/Languages/ILLanguage.cs @@ -30,6 +30,7 @@ using ICSharpCode.Decompiler.Solution; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; using ICSharpCode.ILSpy.TextView; +using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX; namespace ICSharpCode.ILSpy @@ -57,7 +58,7 @@ namespace ICSharpCode.ILSpy protected virtual ReflectionDisassembler CreateDisassembler(ITextOutput output, DecompilationOptions options) { - var displaySettings = MainWindow.Instance.CurrentDisplaySettings; + var displaySettings = SettingsService.Instance.DisplaySettings; output.IndentationString = options.DecompilerSettings.CSharpFormattingOptions.IndentationString; return new ReflectionDisassembler(output, options.CancellationToken) { DetectControlStructure = detectControlStructure, diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index 810e70f99..d87bd31ff 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -83,8 +83,6 @@ namespace ICSharpCode.ILSpy get { return instance; } } - internal AssemblyListManager AssemblyListManager { get; } - public SharpTreeView AssemblyTreeView { get { return FindResource("AssemblyTreeView") as SharpTreeView; @@ -97,40 +95,28 @@ namespace ICSharpCode.ILSpy } } - public DecompilerSettings CurrentDecompilerSettings { get; internal set; } - - public DisplaySettingsViewModel CurrentDisplaySettings { get; internal set; } - public DecompilationOptions CreateDecompilationOptions() { var decompilerView = DockWorkspace.Instance.ActiveTabPage.Content as IProgress; - return new DecompilationOptions(CurrentLanguageVersion, CurrentDecompilerSettings, CurrentDisplaySettings) { Progress = decompilerView }; + return new DecompilationOptions(CurrentLanguageVersion, SettingsService.Instance.DecompilerSettings, SettingsService.Instance.DisplaySettings) { Progress = decompilerView }; } public MainWindow() { instance = this; - var spySettings = SettingsService.Instance.SpySettings; var sessionSettings = SettingsService.Instance.SessionSettings; - this.CurrentDecompilerSettings = DecompilerSettingsPanel.LoadDecompilerSettings(spySettings); - this.CurrentDisplaySettings = DisplaySettingsPanel.LoadDisplaySettings(spySettings); - this.AssemblyListManager = new AssemblyListManager(spySettings) { - ApplyWinRTProjections = CurrentDecompilerSettings.ApplyWindowsRuntimeProjections, - UseDebugSymbols = CurrentDecompilerSettings.UseDebugSymbols - }; - // Make sure Images are initialized on the UI thread. this.Icon = Images.ILSpyIcon; this.DataContext = new MainWindowViewModel { Workspace = DockWorkspace.Instance, SessionSettings = sessionSettings, - AssemblyListManager = AssemblyListManager + AssemblyListManager = SettingsService.Instance.AssemblyListManager }; - AssemblyListManager.CreateDefaultAssemblyLists(); + SettingsService.Instance.AssemblyListManager.CreateDefaultAssemblyLists(); if (!string.IsNullOrEmpty(sessionSettings.CurrentCulture)) { System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(sessionSettings.CurrentCulture); @@ -840,12 +826,12 @@ namespace ICSharpCode.ILSpy { // Load AssemblyList only in Loaded event so that WPF is initialized before we start the CPU-heavy stuff. // This makes the UI come up a bit faster. - this.assemblyList = AssemblyListManager.LoadList(sessionSettings.ActiveAssemblyList); + this.assemblyList = SettingsService.Instance.AssemblyListManager.LoadList(sessionSettings.ActiveAssemblyList); } else { - AssemblyListManager.ClearAll(); - this.assemblyList = AssemblyListManager.CreateList(AssemblyListManager.DefaultListName); + SettingsService.Instance.AssemblyListManager.ClearAll(); + this.assemblyList = SettingsService.Instance.AssemblyListManager.CreateList(AssemblyListManager.DefaultListName); } HandleCommandLineArguments(App.CommandLineArguments); @@ -877,10 +863,10 @@ namespace ICSharpCode.ILSpy DockWorkspace.Instance.ShowText(output); } - bool FormatExceptions(App.ExceptionData[] exceptions, ITextOutput output) + static bool FormatExceptions(App.ExceptionData[] exceptions, ITextOutput output) { var stringBuilder = new StringBuilder(); - var result = FormatExceptions(exceptions, stringBuilder); + var result = exceptions.FormatExceptions(stringBuilder); if (result) { output.Write(stringBuilder.ToString()); @@ -888,35 +874,6 @@ namespace ICSharpCode.ILSpy return result; } - internal static bool FormatExceptions(App.ExceptionData[] exceptions, StringBuilder output) - { - if (exceptions.Length == 0) - return false; - bool first = true; - - foreach (var item in exceptions) - { - if (first) - first = false; - else - output.AppendLine("-------------------------------------------------"); - output.AppendLine("Error(s) loading plugin: " + item.PluginName); - if (item.Exception is System.Reflection.ReflectionTypeLoadException) - { - var e = (System.Reflection.ReflectionTypeLoadException)item.Exception; - foreach (var ex in e.LoaderExceptions) - { - output.AppendLine(ex.ToString()); - output.AppendLine(); - } - } - else - output.AppendLine(item.Exception.ToString()); - } - - return true; - } - #region Update Check string updateAvailableDownloadUrl; @@ -974,7 +931,7 @@ namespace ICSharpCode.ILSpy public void ShowAssemblyList(string name) { - AssemblyList list = this.AssemblyListManager.LoadList(name); + AssemblyList list = SettingsService.Instance.AssemblyListManager.LoadList(name); //Only load a new list when it is a different one if (list.ListName != CurrentAssemblyList.ListName) { @@ -1388,7 +1345,7 @@ namespace ICSharpCode.ILSpy { refreshInProgress = true; var path = GetPathForNode(AssemblyTreeView.SelectedItem as SharpTreeNode); - ShowAssemblyList(AssemblyListManager.LoadList(assemblyList.ListName)); + ShowAssemblyList(SettingsService.Instance.AssemblyListManager.LoadList(assemblyList.ListName)); SelectNode(FindNodeByPath(path, true), inNewTabPage: false, AssemblyTreeView.IsFocused); } finally diff --git a/ILSpy/Metadata/DebugMetadataTablesTreeNode.cs b/ILSpy/Metadata/DebugMetadataTablesTreeNode.cs index af8dd974a..9ec211751 100644 --- a/ILSpy/Metadata/DebugMetadataTablesTreeNode.cs +++ b/ILSpy/Metadata/DebugMetadataTablesTreeNode.cs @@ -21,6 +21,7 @@ using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.ILSpy.TreeNodes; +using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpy.ViewModels; namespace ICSharpCode.ILSpy.Metadata @@ -58,7 +59,7 @@ namespace ICSharpCode.ILSpy.Metadata if (ShowTable(TableIndex.CustomDebugInformation)) this.Children.Add(new CustomDebugInformationTableTreeNode(metadataFile)); - bool ShowTable(TableIndex table) => !MainWindow.Instance.CurrentDisplaySettings.HideEmptyMetadataTables || metadataFile.Metadata.GetTableRowCount(table) > 0; + bool ShowTable(TableIndex table) => !SettingsService.Instance.DisplaySettings.HideEmptyMetadataTables || metadataFile.Metadata.GetTableRowCount(table) > 0; } public override bool View(TabPageModel tabPage) diff --git a/ILSpy/Metadata/MetadataTablesTreeNode.cs b/ILSpy/Metadata/MetadataTablesTreeNode.cs index 26bb59b21..1572ed4e3 100644 --- a/ILSpy/Metadata/MetadataTablesTreeNode.cs +++ b/ILSpy/Metadata/MetadataTablesTreeNode.cs @@ -23,6 +23,7 @@ using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.ILSpy.TreeNodes; +using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpy.ViewModels; namespace ICSharpCode.ILSpy.Metadata @@ -50,7 +51,7 @@ namespace ICSharpCode.ILSpy.Metadata } } - internal static bool ShowTable(TableIndex table, MetadataReader metadata) => !MainWindow.Instance.CurrentDisplaySettings.HideEmptyMetadataTables || metadata.GetTableRowCount(table) > 0; + internal static bool ShowTable(TableIndex table, MetadataReader metadata) => !SettingsService.Instance.DisplaySettings.HideEmptyMetadataTables || metadata.GetTableRowCount(table) > 0; internal static MetadataTableTreeNode CreateTableTreeNode(TableIndex table, MetadataFile metadataFile) { diff --git a/ILSpy/Options/DecompilerSettingsPanel.xaml.cs b/ILSpy/Options/DecompilerSettingsPanel.xaml.cs index 8a55d73fb..d530079dc 100644 --- a/ILSpy/Options/DecompilerSettingsPanel.xaml.cs +++ b/ILSpy/Options/DecompilerSettingsPanel.xaml.cs @@ -19,6 +19,7 @@ using System.ComponentModel.Composition; using System.Xml.Linq; +using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX.Settings; namespace ICSharpCode.ILSpy.Options @@ -50,15 +51,15 @@ namespace ICSharpCode.ILSpy.Options var newSettings = ((DecompilerSettingsViewModel)this.DataContext).ToDecompilerSettings(); ISettingsProvider.SaveDecompilerSettings(root, newSettings); - MainWindow.Instance.CurrentDecompilerSettings = newSettings; - MainWindow.Instance.AssemblyListManager.ApplyWinRTProjections = newSettings.ApplyWindowsRuntimeProjections; - MainWindow.Instance.AssemblyListManager.UseDebugSymbols = newSettings.UseDebugSymbols; + SettingsService.Instance.DecompilerSettings = newSettings; + SettingsService.Instance.AssemblyListManager.ApplyWinRTProjections = newSettings.ApplyWindowsRuntimeProjections; + SettingsService.Instance.AssemblyListManager.UseDebugSymbols = newSettings.UseDebugSymbols; } public void LoadDefaults() { - MainWindow.Instance.CurrentDecompilerSettings = new(); - this.DataContext = new DecompilerSettingsViewModel(MainWindow.Instance.CurrentDecompilerSettings); + SettingsService.Instance.DecompilerSettings = new(); + this.DataContext = new DecompilerSettingsViewModel(SettingsService.Instance.DecompilerSettings); } } } diff --git a/ILSpy/Options/DisplaySettingsPanel.xaml.cs b/ILSpy/Options/DisplaySettingsPanel.xaml.cs index 3e81eb7fe..8358a3cf5 100644 --- a/ILSpy/Options/DisplaySettingsPanel.xaml.cs +++ b/ILSpy/Options/DisplaySettingsPanel.xaml.cs @@ -99,7 +99,7 @@ namespace ICSharpCode.ILSpy.Options select ff).ToArray(); } - public static DisplaySettingsViewModel LoadDisplaySettings(ILSpySettings settings) + public static DisplaySettingsViewModel LoadDisplaySettings(ILSpySettings settings, SessionSettings sessionSettings = null) { XElement e = settings["DisplaySettings"]; var s = new DisplaySettingsViewModel(); @@ -124,7 +124,7 @@ namespace ICSharpCode.ILSpy.Options s.ShowRawOffsetsAndBytesBeforeInstruction = (bool?)e.Attribute("ShowRawOffsetsAndBytesBeforeInstruction") ?? false; s.StyleWindowTitleBar = (bool?)e.Attribute("StyleWindowTitleBar") ?? false; - s.Theme = SettingsService.Instance.SessionSettings.Theme; + s.Theme = (sessionSettings ?? SettingsService.Instance.SessionSettings).Theme; return s; } @@ -158,7 +158,7 @@ namespace ICSharpCode.ILSpy.Options SettingsService.Instance.SessionSettings.Theme = s.Theme; var sessionSettings = SettingsService.Instance.SessionSettings.ToXml(); - MainWindow.Instance.CurrentDisplaySettings.CopyValues(s); + SettingsService.Instance.DisplaySettings.CopyValues(s); Update(section); Update(sessionSettings); @@ -190,8 +190,8 @@ namespace ICSharpCode.ILSpy.Options public void LoadDefaults() { - MainWindow.Instance.CurrentDisplaySettings.CopyValues(new DisplaySettingsViewModel()); - this.DataContext = MainWindow.Instance.CurrentDisplaySettings; + SettingsService.Instance.DisplaySettings.CopyValues(new DisplaySettingsViewModel()); + this.DataContext = SettingsService.Instance.DisplaySettings; } } diff --git a/ILSpy/Search/SearchPane.cs b/ILSpy/Search/SearchPane.cs index f3bd6a550..7c0a2acd9 100644 --- a/ILSpy/Search/SearchPane.cs +++ b/ILSpy/Search/SearchPane.cs @@ -243,7 +243,7 @@ namespace ICSharpCode.ILSpy.Search } MainWindow mainWindow = MainWindow.Instance; - resultsComparer = mainWindow.CurrentDisplaySettings.SortResults ? + resultsComparer = SettingsService.Instance.DisplaySettings.SortResults ? SearchResult.ComparerByFitness : SearchResult.ComparerByName; Results.Clear(); @@ -454,7 +454,7 @@ namespace ICSharpCode.ILSpy.Search request.RegEx = regex; request.SearchResultFactory = new SearchResultFactory(language); request.TreeNodeFactory = new TreeNodeFactory(); - request.DecompilerSettings = MainWindow.Instance.CurrentDecompilerSettings; + request.DecompilerSettings = SettingsService.Instance.DecompilerSettings; return request; } diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 5d7eb069e..8aaa005ec 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -113,9 +113,9 @@ namespace ICSharpCode.ILSpy.TextView textEditor.TextArea.Caret.PositionChanged += HighlightBrackets; textEditor.MouseMove += TextEditorMouseMove; textEditor.MouseLeave += TextEditorMouseLeave; - textEditor.SetBinding(Control.FontFamilyProperty, new Binding { Source = MainWindow.Instance.CurrentDisplaySettings, Path = new PropertyPath("SelectedFont") }); - textEditor.SetBinding(Control.FontSizeProperty, new Binding { Source = MainWindow.Instance.CurrentDisplaySettings, Path = new PropertyPath("SelectedFontSize") }); - textEditor.SetBinding(TextEditor.WordWrapProperty, new Binding { Source = MainWindow.Instance.CurrentDisplaySettings, Path = new PropertyPath("EnableWordWrap") }); + textEditor.SetBinding(Control.FontFamilyProperty, new Binding { Source = SettingsService.Instance.DisplaySettings, Path = new PropertyPath("SelectedFont") }); + textEditor.SetBinding(Control.FontSizeProperty, new Binding { Source = SettingsService.Instance.DisplaySettings, Path = new PropertyPath("SelectedFontSize") }); + textEditor.SetBinding(TextEditor.WordWrapProperty, new Binding { Source = SettingsService.Instance.DisplaySettings, Path = new PropertyPath("EnableWordWrap") }); // disable Tab editing command (useless for read-only editor); allow using tab for focus navigation instead RemoveEditCommand(EditingCommands.TabForward); @@ -125,7 +125,7 @@ namespace ICSharpCode.ILSpy.TextView textEditor.TextArea.TextView.BackgroundRenderers.Add(textMarkerService); textEditor.TextArea.TextView.LineTransformers.Add(textMarkerService); textEditor.ShowLineNumbers = true; - MainWindow.Instance.CurrentDisplaySettings.PropertyChanged += CurrentDisplaySettings_PropertyChanged; + SettingsService.Instance.DisplaySettings.PropertyChanged += CurrentDisplaySettings_PropertyChanged; // SearchPanel SearchPanel searchPanel = SearchPanel.Install(textEditor.TextArea); @@ -197,14 +197,14 @@ namespace ICSharpCode.ILSpy.TextView { if (margin is LineNumberMargin || margin is System.Windows.Shapes.Line) { - margin.Visibility = MainWindow.Instance.CurrentDisplaySettings.ShowLineNumbers ? Visibility.Visible : Visibility.Collapsed; + margin.Visibility = SettingsService.Instance.DisplaySettings.ShowLineNumbers ? Visibility.Visible : Visibility.Collapsed; } } } void SetHighlightCurrentLine() { - textEditor.Options.HighlightCurrentLine = MainWindow.Instance.CurrentDisplaySettings.HighlightCurrentLine; + textEditor.Options.HighlightCurrentLine = SettingsService.Instance.DisplaySettings.HighlightCurrentLine; } #endregion @@ -485,7 +485,7 @@ namespace ICSharpCode.ILSpy.TextView public FlowDocumentTooltip(FlowDocument document) { TextOptions.SetTextFormattingMode(this, TextFormattingMode.Display); - double fontSize = MainWindow.Instance.CurrentDisplaySettings.SelectedFontSize; + double fontSize = SettingsService.Instance.DisplaySettings.SelectedFontSize; viewer = new FlowDocumentScrollViewer() { Width = document.MinPageWidth + fontSize * 5, MaxWidth = MainWindow.Instance.ActualWidth @@ -532,7 +532,7 @@ namespace ICSharpCode.ILSpy.TextView #region Highlight brackets void HighlightBrackets(object? sender, EventArgs e) { - if (MainWindow.Instance.CurrentDisplaySettings.HighlightMatchingBraces) + if (SettingsService.Instance.DisplaySettings.HighlightMatchingBraces) { var result = SettingsService.Instance.SessionSettings.LanguageSettings.Language.BracketSearcher.SearchBracket(textEditor.Document, textEditor.CaretOffset); bracketHighlightRenderer.SetHighlight(result); @@ -754,7 +754,7 @@ namespace ICSharpCode.ILSpy.TextView { if (state != null) { - state.RestoreFoldings(textOutput.Foldings, MainWindow.Instance.CurrentDisplaySettings.ExpandMemberDefinitions); + state.RestoreFoldings(textOutput.Foldings, SettingsService.Instance.DisplaySettings.ExpandMemberDefinitions); textEditor.ScrollToVerticalOffset(state.VerticalOffset); textEditor.ScrollToHorizontalOffset(state.HorizontalOffset); } @@ -778,7 +778,7 @@ namespace ICSharpCode.ILSpy.TextView } currentAddress = textOutput.Address; currentTitle = textOutput.Title; - expandMemberDefinitions = MainWindow.Instance.CurrentDisplaySettings.ExpandMemberDefinitions; + expandMemberDefinitions = SettingsService.Instance.DisplaySettings.ExpandMemberDefinitions; } #endregion @@ -1289,7 +1289,7 @@ namespace ICSharpCode.ILSpy.TextView public void Dispose() { - MainWindow.Instance.CurrentDisplaySettings.PropertyChanged -= CurrentDisplaySettings_PropertyChanged; + SettingsService.Instance.DisplaySettings.PropertyChanged -= CurrentDisplaySettings_PropertyChanged; } #region Unfold diff --git a/ILSpy/TextView/DocumentationUIBuilder.cs b/ILSpy/TextView/DocumentationUIBuilder.cs index 41cad9d61..bc7a9a409 100644 --- a/ILSpy/TextView/DocumentationUIBuilder.cs +++ b/ILSpy/TextView/DocumentationUIBuilder.cs @@ -37,6 +37,7 @@ using ICSharpCode.Decompiler.Documentation; using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.Options; +using ICSharpCode.ILSpy.Util; namespace ICSharpCode.ILSpy.TextView { @@ -115,7 +116,7 @@ namespace ICSharpCode.ILSpy.TextView // Paragraph sadly does not support TextWrapping.NoWrap var text = new TextBlock { FontFamily = GetCodeFont(), - FontSize = MainWindow.Instance.CurrentDisplaySettings.SelectedFontSize, + FontSize = SettingsService.Instance.DisplaySettings.SelectedFontSize, TextAlignment = TextAlignment.Left }; text.Inlines.AddRange(richText.CreateRuns(document)); @@ -435,7 +436,7 @@ namespace ICSharpCode.ILSpy.TextView FontFamily GetCodeFont() { - return MainWindow.Instance.CurrentDisplaySettings.SelectedFont; + return SettingsService.Instance.DisplaySettings.SelectedFont; } public void AddInline(Inline inline) diff --git a/ILSpy/Themes/WindowStyleManagerBehavior.cs b/ILSpy/Themes/WindowStyleManagerBehavior.cs index 55d0c2c9b..60c7e6ada 100644 --- a/ILSpy/Themes/WindowStyleManagerBehavior.cs +++ b/ILSpy/Themes/WindowStyleManagerBehavior.cs @@ -24,6 +24,7 @@ using System.Windows.Interop; using System.Windows.Media; using ICSharpCode.ILSpy.Options; +using ICSharpCode.ILSpy.Util; using TomsToolbox.Essentials; using TomsToolbox.Wpf; @@ -42,7 +43,7 @@ namespace ICSharpCode.ILSpy.Themes { base.OnAttached(); - MainWindow.Instance.CurrentDisplaySettings.PropertyChanged += DisplaySettings_PropertyChanged; + SettingsService.Instance.DisplaySettings.PropertyChanged += DisplaySettings_PropertyChanged; _foreground = AssociatedObject.Track(Control.ForegroundProperty); _background = AssociatedObject.Track(Control.BackgroundProperty); @@ -61,7 +62,7 @@ namespace ICSharpCode.ILSpy.Themes _foreground.Changed -= Color_Changed; _background.Changed -= Color_Changed; - MainWindow.Instance.CurrentDisplaySettings.PropertyChanged -= DisplaySettings_PropertyChanged; + SettingsService.Instance.DisplaySettings.PropertyChanged -= DisplaySettings_PropertyChanged; } private void Color_Changed(object sender, EventArgs e) @@ -73,7 +74,7 @@ namespace ICSharpCode.ILSpy.Themes { var window = AssociatedObject; - if (MainWindow.Instance.CurrentDisplaySettings.StyleWindowTitleBar) + if (SettingsService.Instance.DisplaySettings.StyleWindowTitleBar) window.Style = (Style)window.FindResource(TomsToolbox.Wpf.Styles.ResourceKeys.WindowStyle); } @@ -86,7 +87,7 @@ namespace ICSharpCode.ILSpy.Themes { if (e.PropertyName == nameof(DisplaySettingsViewModel.StyleWindowTitleBar)) { - if (!MainWindow.Instance.CurrentDisplaySettings.StyleWindowTitleBar) + if (!SettingsService.Instance.DisplaySettings.StyleWindowTitleBar) { restartNotificationThrottle.Tick(); return; diff --git a/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs b/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs index f0046694b..25602d875 100644 --- a/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs @@ -26,6 +26,7 @@ using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.Themes; +using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions; namespace ICSharpCode.ILSpy.TreeNodes @@ -85,7 +86,7 @@ namespace ICSharpCode.ILSpy.TreeNodes { this.Children.Add(new AssemblyReferenceReferencedTypesTreeNode(module, r)); - var resolver = parentAssembly.LoadedAssembly.GetAssemblyResolver(MainWindow.Instance.CurrentDecompilerSettings.AutoLoadAssemblyReferences); + var resolver = parentAssembly.LoadedAssembly.GetAssemblyResolver(SettingsService.Instance.DecompilerSettings.AutoLoadAssemblyReferences); var referencedModule = resolver.Resolve(r); if (referencedModule != null) { diff --git a/ILSpy/TreeNodes/AssemblyTreeNode.cs b/ILSpy/TreeNodes/AssemblyTreeNode.cs index b28c0809f..d2f102ef2 100644 --- a/ILSpy/TreeNodes/AssemblyTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyTreeNode.cs @@ -33,6 +33,7 @@ using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.Controls.TreeView; using ICSharpCode.ILSpy.Metadata; using ICSharpCode.ILSpy.Properties; +using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpy.ViewModels; using ICSharpCode.ILSpyX; using ICSharpCode.ILSpyX.FileLoaders; @@ -261,7 +262,7 @@ namespace ICSharpCode.ILSpy.TreeNodes ns.Children.Clear(); } namespaces.Clear(); - bool useNestedStructure = MainWindow.Instance.CurrentDisplaySettings.UseNestedNamespaceNodes; + bool useNestedStructure = SettingsService.Instance.DisplaySettings.UseNestedNamespaceNodes; foreach (var type in assembly.TopLevelTypeDefinitions.OrderBy(t => t.ReflectionName, NaturalStringComparer.Instance)) { var ns = GetOrCreateNamespaceTreeNode(type.Namespace); @@ -327,7 +328,7 @@ namespace ICSharpCode.ILSpy.TreeNodes ns.Children.Clear(); } namespaces.Clear(); - bool useNestedStructure = MainWindow.Instance.CurrentDisplaySettings.UseNestedNamespaceNodes; + bool useNestedStructure = SettingsService.Instance.DisplaySettings.UseNestedNamespaceNodes; foreach (var type in assembly.TopLevelTypeDefinitions.OrderBy(t => t.ReflectionName, NaturalStringComparer.Instance)) { var ns = GetOrCreateNamespaceTreeNode(type.Namespace); diff --git a/ILSpy/TreeNodes/ILSpyTreeNode.cs b/ILSpy/TreeNodes/ILSpyTreeNode.cs index d2a3c93dc..e3958a1bb 100644 --- a/ILSpy/TreeNodes/ILSpyTreeNode.cs +++ b/ILSpy/TreeNodes/ILSpyTreeNode.cs @@ -158,11 +158,11 @@ namespace ICSharpCode.ILSpy.TreeNodes protected string GetSuffixString(EntityHandle handle) { - if (!MainWindow.Instance.CurrentDisplaySettings.ShowMetadataTokens) + if (!SettingsService.Instance.DisplaySettings.ShowMetadataTokens) return string.Empty; int token = MetadataTokens.GetToken(handle); - if (MainWindow.Instance.CurrentDisplaySettings.ShowMetadataTokensInBase10) + if (SettingsService.Instance.DisplaySettings.ShowMetadataTokensInBase10) return " @" + token; return " @" + token.ToString("x8"); } diff --git a/ILSpy/Util/SettingsService.cs b/ILSpy/Util/SettingsService.cs index 0f7a07e6a..a55e2f54c 100644 --- a/ILSpy/Util/SettingsService.cs +++ b/ILSpy/Util/SettingsService.cs @@ -1,20 +1,34 @@ -using ICSharpCode.ILSpyX.Settings; +using ICSharpCode.Decompiler; +using ICSharpCode.ILSpy.Options; +using ICSharpCode.ILSpyX; +using ICSharpCode.ILSpyX.Settings; namespace ICSharpCode.ILSpy.Util { - internal class SettingsService + public class SettingsService { - public static readonly SettingsService Instance = new SettingsService(); + public static readonly SettingsService Instance = new(); private SettingsService() { - this.SpySettings = ILSpySettings.Load(); - - SessionSettings = new(this.SpySettings); + SpySettings = ILSpySettings.Load(); + SessionSettings = new(SpySettings); + DecompilerSettings = DecompilerSettingsPanel.LoadDecompilerSettings(SpySettings); + DisplaySettings = DisplaySettingsPanel.LoadDisplaySettings(SpySettings, SessionSettings); + AssemblyListManager = new(SpySettings) { + ApplyWinRTProjections = DecompilerSettings.ApplyWindowsRuntimeProjections, + UseDebugSymbols = DecompilerSettings.UseDebugSymbols + }; } public ILSpySettings SpySettings { get; } public SessionSettings SessionSettings { get; } + + public DecompilerSettings DecompilerSettings { get; set; } + + public DisplaySettingsViewModel DisplaySettings { get; } + + public AssemblyListManager AssemblyListManager { get; } } } diff --git a/ILSpy/ViewModels/ManageAssemblyListsViewModel.cs b/ILSpy/ViewModels/ManageAssemblyListsViewModel.cs index 23959c2c3..ec039552f 100644 --- a/ILSpy/ViewModels/ManageAssemblyListsViewModel.cs +++ b/ILSpy/ViewModels/ManageAssemblyListsViewModel.cs @@ -39,7 +39,7 @@ namespace ICSharpCode.ILSpy.ViewModels public ManageAssemblyListsViewModel(Window parent) { - this.manager = MainWindow.Instance.AssemblyListManager; + this.manager = SettingsService.Instance.AssemblyListManager; this.parent = parent; NewCommand = new DelegateCommand(ExecuteNew); diff --git a/ILSpy/ViewModels/SearchPaneModel.cs b/ILSpy/ViewModels/SearchPaneModel.cs index ea6fca679..d1670f72c 100644 --- a/ILSpy/ViewModels/SearchPaneModel.cs +++ b/ILSpy/ViewModels/SearchPaneModel.cs @@ -33,7 +33,7 @@ namespace ICSharpCode.ILSpy.ViewModels ContentId = PaneContentId; Title = Properties.Resources.SearchPane_Search; Icon = "Images/Search"; - ShortcutKey = new KeyGesture(Key.F, ModifierKeys.Control | ModifierKeys.Shift); + ShortcutKey = new(Key.F, ModifierKeys.Control | ModifierKeys.Shift); IsCloseable = true; } diff --git a/ILSpy/Views/DebugSteps.xaml.cs b/ILSpy/Views/DebugSteps.xaml.cs index c4177e3aa..b619cc6b7 100644 --- a/ILSpy/Views/DebugSteps.xaml.cs +++ b/ILSpy/Views/DebugSteps.xaml.cs @@ -124,7 +124,7 @@ namespace ICSharpCode.ILSpy var window = MainWindow.Instance; var state = DockWorkspace.Instance.ActiveTabPage.GetState(); DockWorkspace.Instance.ActiveTabPage.ShowTextViewAsync(textView => textView.DecompileAsync(window.CurrentLanguage, window.SelectedNodes, - new DecompilationOptions(window.CurrentLanguageVersion, window.CurrentDecompilerSettings, window.CurrentDisplaySettings) { + new DecompilationOptions(window.CurrentLanguageVersion, SettingsService.Instance.DecompilerSettings, SettingsService.Instance.DisplaySettings) { StepLimit = step, IsDebug = isDebug, TextViewState = state as TextView.DecompilerTextViewState From 82d697587fc0dbe0353f5dd51df3df7a672421b8 Mon Sep 17 00:00:00 2001 From: tom-englert Date: Wed, 21 Aug 2024 15:14:14 +0200 Subject: [PATCH 33/35] Fix: Current theme in menu is not selected. --- ILSpy/MainWindow.xaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ILSpy/MainWindow.xaml b/ILSpy/MainWindow.xaml index a6650e05a..5dbf05eb8 100644 --- a/ILSpy/MainWindow.xaml +++ b/ILSpy/MainWindow.xaml @@ -10,7 +10,6 @@ xmlns:avalondock="https://github.com/Dirkster99/AvalonDock" xmlns:controls="clr-namespace:ICSharpCode.ILSpy.Controls" xmlns:docking="clr-namespace:ICSharpCode.ILSpy.Docking" - xmlns:analyzers="clr-namespace:ICSharpCode.ILSpy.Analyzers" xmlns:properties="clr-namespace:ICSharpCode.ILSpy.Properties" Title="ILSpy" MinWidth="250" @@ -79,6 +78,8 @@ + + @@ -137,7 +138,7 @@ - + From dde581adc8e620e096f003c39b605bd9ca8ebbf8 Mon Sep 17 00:00:00 2001 From: tom-englert Date: Sat, 24 Aug 2024 16:21:30 +0200 Subject: [PATCH 34/35] Refactor SearchPane so only the view model is exposed. --- .../Search/AbstractSearchStrategy.cs | 4 +- ILSpy/Commands/ScopeSearchToAssembly.cs | 13 +- ILSpy/Commands/ScopeSearchToNamespace.cs | 14 +- ILSpy/Controls/CultureSelectionConverter.cs | 8 +- ILSpy/Controls/TreeView/Converters.cs | 46 ----- ILSpy/Controls/TreeView/SharpTreeView.xaml | 5 +- ILSpy/DecompilationOptions.cs | 6 +- ILSpy/Docking/DockWorkspace.cs | 11 +- ILSpy/MainWindow.xaml | 126 ++++++------ ILSpy/MainWindow.xaml.cs | 30 +-- ...ettingsViewModel.cs => DisplaySettings.cs} | 6 +- ILSpy/Options/DisplaySettingsPanel.xaml | 2 +- ILSpy/Options/DisplaySettingsPanel.xaml.cs | 8 +- ILSpy/Search/SearchPane.xaml | 24 ++- .../{SearchPane.cs => SearchPane.xaml.cs} | 193 +++++++++--------- .../{ViewModels => Search}/SearchPaneModel.cs | 41 +++- ILSpy/TextView/DecompilerTextView.cs | 4 +- ILSpy/Themes/WindowStyleManagerBehavior.cs | 2 +- ILSpy/Util/SettingsService.cs | 2 +- ILSpy/ViewModels/PaneModel.cs | 103 +++++----- 20 files changed, 328 insertions(+), 320 deletions(-) delete mode 100644 ILSpy/Controls/TreeView/Converters.cs rename ILSpy/Options/{DisplaySettingsViewModel.cs => DisplaySettings.cs} (98%) rename ILSpy/Search/{SearchPane.cs => SearchPane.xaml.cs} (72%) rename ILSpy/{ViewModels => Search}/SearchPaneModel.cs (50%) diff --git a/ICSharpCode.ILSpyX/Search/AbstractSearchStrategy.cs b/ICSharpCode.ILSpyX/Search/AbstractSearchStrategy.cs index 61de7b714..76c3c4d4f 100644 --- a/ICSharpCode.ILSpyX/Search/AbstractSearchStrategy.cs +++ b/ICSharpCode.ILSpyX/Search/AbstractSearchStrategy.cs @@ -52,7 +52,7 @@ namespace ICSharpCode.ILSpyX.Search public AssemblySearchKind AssemblySearchKind; public MemberSearchKind MemberSearchKind; public string[] Keywords; - public Regex RegEx; + public Regex? RegEx; public bool FullNameSearch; public bool OmitGenerics; public string InNamespace; @@ -62,7 +62,7 @@ namespace ICSharpCode.ILSpyX.Search public abstract class AbstractSearchStrategy { protected readonly string[] searchTerm; - protected readonly Regex regex; + protected readonly Regex? regex; protected readonly bool fullNameSearch; protected readonly bool omitGenerics; protected readonly SearchRequest searchRequest; diff --git a/ILSpy/Commands/ScopeSearchToAssembly.cs b/ILSpy/Commands/ScopeSearchToAssembly.cs index 3d8a10397..27f3c79f4 100644 --- a/ILSpy/Commands/ScopeSearchToAssembly.cs +++ b/ILSpy/Commands/ScopeSearchToAssembly.cs @@ -23,6 +23,7 @@ using System.ComponentModel.Composition; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.AppEnv; using ICSharpCode.ILSpy.Properties; +using ICSharpCode.ILSpy.Search; using ICSharpCode.ILSpy.TreeNodes; namespace ICSharpCode.ILSpy @@ -31,11 +32,19 @@ namespace ICSharpCode.ILSpy [PartCreationPolicy(CreationPolicy.Shared)] public class ScopeSearchToAssembly : IContextMenuEntry { + private readonly SearchPaneModel searchPane; + + [ImportingConstructor] + public ScopeSearchToAssembly(SearchPaneModel searchPane) + { + this.searchPane = searchPane; + } + public void Execute(TextViewContext context) { // asmName cannot be null here, because Execute is only called if IsEnabled/IsVisible return true. string asmName = GetAssembly(context)!; - string searchTerm = MainWindow.Instance.SearchPane.SearchTerm; + string searchTerm = searchPane.SearchTerm; string[] args = CommandLineTools.CommandLineToArgumentArray(searchTerm); bool replaced = false; for (int i = 0; i < args.Length; i++) @@ -55,7 +64,7 @@ namespace ICSharpCode.ILSpy { searchTerm = CommandLineTools.ArgumentArrayToCommandLine(args); } - MainWindow.Instance.SearchPane.SearchTerm = searchTerm; + searchPane.SearchTerm = searchTerm; } public bool IsEnabled(TextViewContext context) diff --git a/ILSpy/Commands/ScopeSearchToNamespace.cs b/ILSpy/Commands/ScopeSearchToNamespace.cs index c6be0a9db..645f9d425 100644 --- a/ILSpy/Commands/ScopeSearchToNamespace.cs +++ b/ILSpy/Commands/ScopeSearchToNamespace.cs @@ -21,6 +21,7 @@ using System.ComponentModel.Composition; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.AppEnv; using ICSharpCode.ILSpy.Properties; +using ICSharpCode.ILSpy.Search; using ICSharpCode.ILSpy.TreeNodes; namespace ICSharpCode.ILSpy @@ -29,10 +30,18 @@ namespace ICSharpCode.ILSpy [PartCreationPolicy(CreationPolicy.Shared)] public class ScopeSearchToNamespace : IContextMenuEntry { + private readonly SearchPaneModel searchPane; + + [ImportingConstructor] + public ScopeSearchToNamespace(SearchPaneModel searchPane) + { + this.searchPane = searchPane; + } + public void Execute(TextViewContext context) { string ns = GetNamespace(context); - string searchTerm = MainWindow.Instance.SearchPane.SearchTerm; + string searchTerm = searchPane.SearchTerm; string[] args = CommandLineTools.CommandLineToArgumentArray(searchTerm); bool replaced = false; for (int i = 0; i < args.Length; i++) @@ -52,7 +61,8 @@ namespace ICSharpCode.ILSpy { searchTerm = CommandLineTools.ArgumentArrayToCommandLine(args); } - MainWindow.Instance.SearchPane.SearchTerm = searchTerm; + + searchPane.SearchTerm = searchTerm; } public bool IsEnabled(TextViewContext context) diff --git a/ILSpy/Controls/CultureSelectionConverter.cs b/ILSpy/Controls/CultureSelectionConverter.cs index 0e79cf105..74c38993e 100644 --- a/ILSpy/Controls/CultureSelectionConverter.cs +++ b/ILSpy/Controls/CultureSelectionConverter.cs @@ -19,10 +19,11 @@ using System; using System.Globalization; using System.Windows.Data; +using System.Windows.Markup; namespace ICSharpCode.ILSpy.Controls { - public class CultureSelectionConverter : IValueConverter + public class CultureSelectionConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { @@ -37,5 +38,10 @@ namespace ICSharpCode.ILSpy.Controls return parameter; return Binding.DoNothing; } + + public override object ProvideValue(IServiceProvider serviceProvider) + { + return this; + } } } diff --git a/ILSpy/Controls/TreeView/Converters.cs b/ILSpy/Controls/TreeView/Converters.cs deleted file mode 100644 index 401d9026c..000000000 --- a/ILSpy/Controls/TreeView/Converters.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2020 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.Globalization; -using System.Windows; -using System.Windows.Data; -using System.Windows.Markup; - -namespace ICSharpCode.ILSpy.Controls.TreeView -{ - public class CollapsedWhenFalse : MarkupExtension, IValueConverter - { - public static CollapsedWhenFalse Instance = new CollapsedWhenFalse(); - - public override object ProvideValue(IServiceProvider serviceProvider) - { - return Instance; - } - - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - return (bool)value ? Visibility.Visible : Visibility.Collapsed; - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - throw new NotImplementedException(); - } - } -} diff --git a/ILSpy/Controls/TreeView/SharpTreeView.xaml b/ILSpy/Controls/TreeView/SharpTreeView.xaml index 6e87b9e67..562f4bc51 100644 --- a/ILSpy/Controls/TreeView/SharpTreeView.xaml +++ b/ILSpy/Controls/TreeView/SharpTreeView.xaml @@ -1,7 +1,8 @@ + xmlns:styles="urn:TomsToolbox.Wpf.Styles" + xmlns:toms="urn:TomsToolbox"> @@ -279,4 +291,4 @@ - + \ No newline at end of file diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index d87bd31ff..235594f7a 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -89,12 +89,6 @@ namespace ICSharpCode.ILSpy } } - public SearchPane SearchPane { - get { - return App.ExportProvider.GetExportedValue(); - } - } - public DecompilationOptions CreateDecompilationOptions() { var decompilerView = DockWorkspace.Instance.ActiveTabPage.Content as IProgress; @@ -123,10 +117,10 @@ namespace ICSharpCode.ILSpy } InitializeComponent(); InitToolPanes(); - DockWorkspace.Instance.InitializeLayout(DockManager); + DockWorkspace.Instance.InitializeLayout(dockManager); MessageBus.Subscribers += (sender, e) => SessionSettings_PropertyChanged(sender, e); - MessageBus.Subscribers += (sender, e) => filterSettings_PropertyChanged(sender, e); + MessageBus.Subscribers += (sender, e) => LanguageSettings_PropertyChanged(sender, e); MessageBus.Subscribers += DockWorkspace_ActiveTabPageChanged; InitMainMenu(); @@ -647,8 +641,10 @@ namespace ICSharpCode.ILSpy NavigateOnLaunch(args.NavigateTo, sessionSettings.ActiveTreeViewPath, spySettings, relevantAssemblies); if (args.Search != null) { - SearchPane.SearchTerm = args.Search; - SearchPane.Show(); + var searchPane = App.ExportProvider.GetExportedValue(); + + searchPane.SearchTerm = args.Search; + searchPane.Show(); } } @@ -1004,20 +1000,14 @@ namespace ICSharpCode.ILSpy assemblyList.OpenAssembly(asm.Location); } - void filterSettings_PropertyChanged(object sender, PropertyChangedEventArgs e) + void LanguageSettings_PropertyChanged(object sender, PropertyChangedEventArgs e) { - RefreshTreeViewFilter(); if (e.PropertyName == "Language" || e.PropertyName == "LanguageVersion") { DecompileSelectedNodes(recordHistory: false); } } - public void RefreshTreeViewFilter() - { - SearchPane.UpdateFilter(); - } - internal AssemblyListTreeNode AssemblyListTreeNode { get { return assemblyListTreeNode; } } @@ -1592,7 +1582,7 @@ namespace ICSharpCode.ILSpy sessionSettings.ActiveTreeViewPath = GetPathForNode(AssemblyTreeView.SelectedItem as SharpTreeNode); sessionSettings.ActiveAutoLoadedAssembly = GetAutoLoadedAssemblyNode(AssemblyTreeView.SelectedItem as SharpTreeNode); sessionSettings.WindowBounds = this.RestoreBounds; - sessionSettings.DockLayout.Serialize(new XmlLayoutSerializer(DockManager)); + sessionSettings.DockLayout.Serialize(new XmlLayoutSerializer(dockManager)); sessionSettings.Save(); } @@ -1622,8 +1612,8 @@ namespace ICSharpCode.ILSpy { if (this.statusBar.Visibility == Visibility.Collapsed) this.statusBar.Visibility = Visibility.Visible; - this.StatusLabel.Foreground = foreground; - this.StatusLabel.Text = status; + this.statusLabel.Foreground = foreground; + this.statusLabel.Text = status; } public ItemCollection GetMainMenuItems() diff --git a/ILSpy/Options/DisplaySettingsViewModel.cs b/ILSpy/Options/DisplaySettings.cs similarity index 98% rename from ILSpy/Options/DisplaySettingsViewModel.cs rename to ILSpy/Options/DisplaySettings.cs index 15b54ebe5..f59c31a95 100644 --- a/ILSpy/Options/DisplaySettingsViewModel.cs +++ b/ILSpy/Options/DisplaySettings.cs @@ -27,9 +27,9 @@ namespace ICSharpCode.ILSpy.Options /// /// Description of DisplaySettings. /// - public class DisplaySettingsViewModel : INotifyPropertyChanged + public class DisplaySettings : INotifyPropertyChanged { - public DisplaySettingsViewModel() + public DisplaySettings() { this.theme = ThemeManager.Current.DefaultTheme; this.selectedFont = new FontFamily("Consolas"); @@ -328,7 +328,7 @@ namespace ICSharpCode.ILSpy.Options } } - public void CopyValues(DisplaySettingsViewModel s) + public void CopyValues(DisplaySettings s) { this.Theme = s.Theme; this.SelectedFont = s.selectedFont; diff --git a/ILSpy/Options/DisplaySettingsPanel.xaml b/ILSpy/Options/DisplaySettingsPanel.xaml index 52f5c3ab1..1170a8f5b 100644 --- a/ILSpy/Options/DisplaySettingsPanel.xaml +++ b/ILSpy/Options/DisplaySettingsPanel.xaml @@ -7,7 +7,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:themes="clr-namespace:ICSharpCode.ILSpy.Themes" - d:DataContext="{d:DesignInstance local:DisplaySettingsViewModel}"> + d:DataContext="{d:DesignInstance local:DisplaySettings}"> diff --git a/ILSpy/Options/DisplaySettingsPanel.xaml.cs b/ILSpy/Options/DisplaySettingsPanel.xaml.cs index 8358a3cf5..9645dacc7 100644 --- a/ILSpy/Options/DisplaySettingsPanel.xaml.cs +++ b/ILSpy/Options/DisplaySettingsPanel.xaml.cs @@ -99,10 +99,10 @@ namespace ICSharpCode.ILSpy.Options select ff).ToArray(); } - public static DisplaySettingsViewModel LoadDisplaySettings(ILSpySettings settings, SessionSettings sessionSettings = null) + public static DisplaySettings LoadDisplaySettings(ILSpySettings settings, SessionSettings sessionSettings = null) { XElement e = settings["DisplaySettings"]; - var s = new DisplaySettingsViewModel(); + var s = new DisplaySettings(); s.SelectedFont = new FontFamily((string)e.Attribute("Font") ?? "Consolas"); s.SelectedFontSize = (double?)e.Attribute("FontSize") ?? 10.0 * 4 / 3; s.ShowLineNumbers = (bool?)e.Attribute("ShowLineNumbers") ?? false; @@ -131,7 +131,7 @@ namespace ICSharpCode.ILSpy.Options public void Save(XElement root) { - var s = (DisplaySettingsViewModel)this.DataContext; + var s = (DisplaySettings)this.DataContext; var section = new XElement("DisplaySettings"); section.SetAttributeValue("Font", s.SelectedFont.Source); @@ -190,7 +190,7 @@ namespace ICSharpCode.ILSpy.Options public void LoadDefaults() { - SettingsService.Instance.DisplaySettings.CopyValues(new DisplaySettingsViewModel()); + SettingsService.Instance.DisplaySettings.CopyValues(new DisplaySettings()); this.DataContext = SettingsService.Instance.DisplaySettings; } } diff --git a/ILSpy/Search/SearchPane.xaml b/ILSpy/Search/SearchPane.xaml index f08a8d014..518f87e76 100644 --- a/ILSpy/Search/SearchPane.xaml +++ b/ILSpy/Search/SearchPane.xaml @@ -2,10 +2,14 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:controls="clr-namespace:ICSharpCode.ILSpy.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:properties="clr-namespace:ICSharpCode.ILSpy.Properties" + xmlns:properties="clr-namespace:ICSharpCode.ILSpy.Properties" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Name="self" mc:Ignorable="d" xmlns:toms="urn:TomsToolbox" - d:DesignHeight="300" d:DesignWidth="300"> + xmlns:search="clr-namespace:ICSharpCode.ILSpy.Search" + xmlns:spyX="clr-namespace:ICSharpCode.ILSpyX.Search;assembly=ICSharpCode.ILSpyX" + xmlns:viewModels="clr-namespace:ICSharpCode.ILSpy.ViewModels" + d:DesignHeight="300" d:DesignWidth="300" d:DataContext="{d:DesignInstance search:SearchPaneModel}" + viewModels:Pane.IsActive="{Binding IsActive}"> @@ -20,15 +24,19 @@ [DataTemplate(typeof(SearchPaneModel))] - [PartCreationPolicy(CreationPolicy.Shared)] - [Export] - public partial class SearchPane : UserControl + [PartCreationPolicy(CreationPolicy.NonShared)] + public partial class SearchPane { const int MAX_RESULTS = 1000; - const int MAX_REFRESH_TIME_MS = 10; // More means quicker forward of data, less means better responsibility + const int MAX_REFRESH_TIME_MS = 10; // More means quicker forward of data, fewer means better responsibility RunningSearch currentSearch; bool runSearchOnNextShow; IComparer resultsComparer; - public static readonly DependencyProperty ResultsProperty = - DependencyProperty.Register("Results", typeof(ObservableCollection), typeof(SearchPane), - new PropertyMetadata(new ObservableCollection())); - public ObservableCollection Results { - get { return (ObservableCollection)GetValue(ResultsProperty); } - } + public ObservableCollection Results { get; } = []; + + string SearchTerm => searchBox.Text; public SearchPane() { InitializeComponent(); - searchModeComboBox.Items.Add(new { Image = Images.Library, Name = "Types and Members" }); - searchModeComboBox.Items.Add(new { Image = Images.Class, Name = "Type" }); - searchModeComboBox.Items.Add(new { Image = Images.Property, Name = "Member" }); - searchModeComboBox.Items.Add(new { Image = Images.Method, Name = "Method" }); - searchModeComboBox.Items.Add(new { Image = Images.Field, Name = "Field" }); - searchModeComboBox.Items.Add(new { Image = Images.Property, Name = "Property" }); - searchModeComboBox.Items.Add(new { Image = Images.Event, Name = "Event" }); - searchModeComboBox.Items.Add(new { Image = Images.Literal, Name = "Constant" }); - searchModeComboBox.Items.Add(new { Image = Images.Library, Name = "Metadata Token" }); - searchModeComboBox.Items.Add(new { Image = Images.Resource, Name = "Resource" }); - searchModeComboBox.Items.Add(new { Image = Images.Assembly, Name = "Assembly" }); - searchModeComboBox.Items.Add(new { Image = Images.Namespace, Name = "Namespace" }); ContextMenuProvider.Add(listBox); - MessageBus.Subscribers += (sender, e) => MainWindow_Instance_CurrentAssemblyListChanged(sender, e); - CompositionTarget.Rendering += UpdateResults; + MessageBus.Subscribers += (sender, e) => CurrentAssemblyList_Changed(); + MessageBus.Subscribers += (sender, e) => LanguageSettings_PropertyChanged(); - // This starts empty search right away, so do at the end (we're still in ctor) - searchModeComboBox.SelectedIndex = (int)SettingsService.Instance.SessionSettings.SelectedSearchMode; + CompositionTarget.Rendering += UpdateResults; } - void MainWindow_Instance_CurrentAssemblyListChanged(object sender, NotifyCollectionChangedEventArgs e) + void CurrentAssemblyList_Changed() { if (IsVisible) { @@ -103,7 +85,12 @@ namespace ICSharpCode.ILSpy.Search } } - internal void UpdateFilter() + void LanguageSettings_PropertyChanged() + { + UpdateFilter(); + } + + void UpdateFilter() { if (IsVisible) { @@ -116,43 +103,48 @@ namespace ICSharpCode.ILSpy.Search } } - public void Show() + protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) { - if (!IsVisible) + base.OnPropertyChanged(e); + + if (e.Property == IsVisibleProperty) { - DockWorkspace.Instance.ToolPanes.Single(p => p.ContentId == SearchPaneModel.PaneContentId).IsVisible = true; - if (runSearchOnNextShow) - { - runSearchOnNextShow = false; - StartSearch(this.SearchTerm); - } + if (e.NewValue as bool? != true) + return; + + if (!runSearchOnNextShow) + return; + + runSearchOnNextShow = false; + StartSearch(this.SearchTerm); } - Dispatcher.BeginInvoke( - DispatcherPriority.Background, - new Action( - delegate { - searchBox.Focus(); - searchBox.SelectAll(); - })); - } + else if (e.Property == Pane.IsActiveProperty) + { + if (e.NewValue as bool? != true) + return; - public static readonly DependencyProperty SearchTermProperty = - DependencyProperty.Register("SearchTerm", typeof(string), typeof(SearchPane), - new FrameworkPropertyMetadata(string.Empty, OnSearchTermChanged)); + if (IsMouseOver && Mouse.LeftButton == MouseButtonState.Pressed && !SearchTerm.IsNullOrEmpty()) + return; - public string SearchTerm { - get { return (string)GetValue(SearchTermProperty); } - set { SetValue(SearchTermProperty, value); } + FocusSearchBox(); + } } - static void OnSearchTermChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + void FocusSearchBox() { - ((SearchPane)o).StartSearch((string)e.NewValue); + this.BeginInvoke(DispatcherPriority.Background, () => { + searchBox.Focus(); + searchBox.SelectAll(); + }); + } + + void SearchBox_TextChanged(object sender, TextChangedEventArgs e) + { + StartSearch(searchBox.Text); } void SearchModeComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { - SettingsService.Instance.SessionSettings.SelectedSearchMode = (SearchMode)searchModeComboBox.SelectedIndex; StartSearch(this.SearchTerm); } @@ -180,31 +172,35 @@ namespace ICSharpCode.ILSpy.Search protected override void OnKeyDown(KeyEventArgs e) { base.OnKeyDown(e); - if (e.Key == Key.T && e.KeyboardDevice.Modifiers == ModifierKeys.Control) - { - searchModeComboBox.SelectedIndex = (int)SearchMode.Type; - e.Handled = true; - } - else if (e.Key == Key.M && e.KeyboardDevice.Modifiers == ModifierKeys.Control) - { - searchModeComboBox.SelectedIndex = (int)SearchMode.Member; - e.Handled = true; - } - else if (e.Key == Key.S && e.KeyboardDevice.Modifiers == ModifierKeys.Control) + + if (e.KeyboardDevice.Modifiers != ModifierKeys.Control) + return; + + switch (e.Key) { - searchModeComboBox.SelectedIndex = (int)SearchMode.Literal; - e.Handled = true; + case Key.T: + searchModeComboBox.SelectedValue = SearchMode.Type; + e.Handled = true; + break; + case Key.M: + searchModeComboBox.SelectedValue = SearchMode.Member; + e.Handled = true; + break; + case Key.S: + searchModeComboBox.SelectedValue = SearchMode.Literal; + e.Handled = true; + break; } } void SearchBox_PreviewKeyDown(object sender, KeyEventArgs e) { - if (e.Key == Key.Down && listBox.HasItems) - { - e.Handled = true; - listBox.MoveFocus(new TraversalRequest(FocusNavigationDirection.First)); - listBox.SelectedIndex = 0; - } + if (e.Key != Key.Down || !listBox.HasItems) + return; + + e.Handled = true; + listBox.MoveFocus(new(FocusNavigationDirection.First)); + listBox.SelectedIndex = 0; } void UpdateResults(object sender, EventArgs e) @@ -220,18 +216,19 @@ namespace ICSharpCode.ILSpy.Search ++resultsAdded; } - if (resultsAdded > 0 && Results.Count == MAX_RESULTS) - { - Results.Add(new SearchResult { - Name = Properties.Resources.SearchAbortedMoreThan1000ResultsFound, - Location = null!, - Assembly = null!, - Image = null!, - LocationImage = null!, - AssemblyImage = null!, - }); - currentSearch.Cancel(); - } + if (resultsAdded <= 0 || Results.Count != MAX_RESULTS) + return; + + Results.Add(new() { + Name = Properties.Resources.SearchAbortedMoreThan1000ResultsFound, + Location = null!, + Assembly = null!, + Image = null!, + LocationImage = null!, + AssemblyImage = null!, + }); + + currentSearch.Cancel(); } async void StartSearch(string searchTerm) @@ -253,7 +250,7 @@ namespace ICSharpCode.ILSpy.Search { searchProgressBar.IsIndeterminate = true; - startedSearch = new RunningSearch(await mainWindow.CurrentAssemblyList.GetAllAssemblies(), searchTerm, + startedSearch = new(await mainWindow.CurrentAssemblyList.GetAllAssemblies(), searchTerm, (SearchMode)searchModeComboBox.SelectedIndex, mainWindow.CurrentLanguage, SettingsService.Instance.SessionSettings.LanguageSettings.ShowApiLevel); currentSearch = startedSearch; @@ -262,7 +259,8 @@ namespace ICSharpCode.ILSpy.Search } if (currentSearch == startedSearch) - { //are we still running the same search + { + //are we still running the same search searchProgressBar.IsIndeterminate = false; } } @@ -277,7 +275,7 @@ namespace ICSharpCode.ILSpy.Search sealed class RunningSearch { - readonly CancellationTokenSource cts = new CancellationTokenSource(); + readonly CancellationTokenSource cts = new(); readonly IList assemblies; readonly SearchRequest searchRequest; readonly SearchMode searchMode; @@ -299,8 +297,8 @@ namespace ICSharpCode.ILSpy.Search { string[] parts = CommandLineTools.CommandLineToArgumentArray(input); - SearchRequest request = new SearchRequest(); - List keywords = new List(); + SearchRequest request = new(); + List keywords = new(); Regex regex = null; request.Mode = searchMode; @@ -350,7 +348,6 @@ namespace ICSharpCode.ILSpy.Search // then we do not interpret it as prefix, but as searchTerm. searchTerm = part; prefix = null; - prefixLength = -1; } if (prefix == null || prefix.Length <= 2) @@ -442,7 +439,7 @@ namespace ICSharpCode.ILSpy.Search { try { - return new Regex(s, RegexOptions.Compiled); + return new(s, RegexOptions.Compiled); } catch (ArgumentException) { @@ -540,9 +537,11 @@ namespace ICSharpCode.ILSpy.Search public ShowSearchCommand() : base(NavigationCommands.Search) { - NavigationCommands.Search.InputGestures.Clear(); - NavigationCommands.Search.InputGestures.Add(new KeyGesture(Key.F, ModifierKeys.Control | ModifierKeys.Shift)); - NavigationCommands.Search.InputGestures.Add(new KeyGesture(Key.E, ModifierKeys.Control)); + var gestures = NavigationCommands.Search.InputGestures; + + gestures.Clear(); + gestures.Add(new KeyGesture(Key.F, ModifierKeys.Control | ModifierKeys.Shift)); + gestures.Add(new KeyGesture(Key.E, ModifierKeys.Control)); } } } \ No newline at end of file diff --git a/ILSpy/ViewModels/SearchPaneModel.cs b/ILSpy/Search/SearchPaneModel.cs similarity index 50% rename from ILSpy/ViewModels/SearchPaneModel.cs rename to ILSpy/Search/SearchPaneModel.cs index d1670f72c..6ddcd9f04 100644 --- a/ILSpy/ViewModels/SearchPaneModel.cs +++ b/ILSpy/Search/SearchPaneModel.cs @@ -17,15 +17,28 @@ // DEALINGS IN THE SOFTWARE. using System.ComponentModel.Composition; -using System.Windows; using System.Windows.Input; +using System.Windows.Media; -namespace ICSharpCode.ILSpy.ViewModels +using ICSharpCode.ILSpy.Util; +using ICSharpCode.ILSpy.ViewModels; +using ICSharpCode.ILSpyX.Search; + +namespace ICSharpCode.ILSpy.Search { + public class SearchModeModel + { + public SearchMode Mode { get; init; } + public string Name { get; init; } + public ImageSource Image { get; init; } + } + [ExportToolPane] [PartCreationPolicy(CreationPolicy.Shared)] + [Export] public class SearchPaneModel : ToolPaneModel { + private string searchTerm; public const string PaneContentId = "searchPane"; public SearchPaneModel() @@ -37,10 +50,26 @@ namespace ICSharpCode.ILSpy.ViewModels IsCloseable = true; } - public override void Show() - { - base.Show(); - MainWindow.Instance.SearchPane.Show(); + public SearchModeModel[] SearchModes { get; } = [ + new() { Mode = SearchMode.TypeAndMember, Image = Images.Library, Name = "Types and Members" }, + new() { Mode = SearchMode.Type, Image = Images.Class, Name = "Type" }, + new() { Mode = SearchMode.Member, Image = Images.Property, Name = "Member" }, + new() { Mode = SearchMode.Method, Image = Images.Method, Name = "Method" }, + new() { Mode = SearchMode.Field, Image = Images.Field, Name = "Field" }, + new() { Mode = SearchMode.Property, Image = Images.Property, Name = "Property" }, + new() { Mode = SearchMode.Event, Image = Images.Event, Name = "Event" }, + new() { Mode = SearchMode.Literal, Image = Images.Literal, Name = "Constant" }, + new() { Mode = SearchMode.Token, Image = Images.Library, Name = "Metadata Token" }, + new() { Mode = SearchMode.Resource, Image = Images.Resource, Name = "Resource" }, + new() { Mode = SearchMode.Assembly, Image = Images.Assembly, Name = "Assembly" }, + new() { Mode = SearchMode.Namespace, Image = Images.Namespace, Name = "Namespace" } + ]; + + public SessionSettings SessionSettings => SettingsService.Instance.SessionSettings; + + public string SearchTerm { + get => searchTerm; + set => SetProperty(ref searchTerm, value); } } } diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 8aaa005ec..1124a73e5 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -181,11 +181,11 @@ namespace ICSharpCode.ILSpy.TextView void CurrentDisplaySettings_PropertyChanged(object? sender, PropertyChangedEventArgs e) { - if (e.PropertyName == nameof(DisplaySettingsViewModel.ShowLineNumbers)) + if (e.PropertyName == nameof(DisplaySettings.ShowLineNumbers)) { ShowLineMargin(); } - else if (e.PropertyName == nameof(DisplaySettingsViewModel.HighlightCurrentLine)) + else if (e.PropertyName == nameof(DisplaySettings.HighlightCurrentLine)) { SetHighlightCurrentLine(); } diff --git a/ILSpy/Themes/WindowStyleManagerBehavior.cs b/ILSpy/Themes/WindowStyleManagerBehavior.cs index 60c7e6ada..60e771f1a 100644 --- a/ILSpy/Themes/WindowStyleManagerBehavior.cs +++ b/ILSpy/Themes/WindowStyleManagerBehavior.cs @@ -85,7 +85,7 @@ namespace ICSharpCode.ILSpy.Themes private void DisplaySettings_PropertyChanged(object sender, PropertyChangedEventArgs e) { - if (e.PropertyName == nameof(DisplaySettingsViewModel.StyleWindowTitleBar)) + if (e.PropertyName == nameof(DisplaySettings.StyleWindowTitleBar)) { if (!SettingsService.Instance.DisplaySettings.StyleWindowTitleBar) { diff --git a/ILSpy/Util/SettingsService.cs b/ILSpy/Util/SettingsService.cs index a55e2f54c..9068733f3 100644 --- a/ILSpy/Util/SettingsService.cs +++ b/ILSpy/Util/SettingsService.cs @@ -27,7 +27,7 @@ namespace ICSharpCode.ILSpy.Util public DecompilerSettings DecompilerSettings { get; set; } - public DisplaySettingsViewModel DisplaySettings { get; } + public DisplaySettings DisplaySettings { get; } public AssemblyListManager AssemblyListManager { get; } } diff --git a/ILSpy/ViewModels/PaneModel.cs b/ILSpy/ViewModels/PaneModel.cs index c88bf869a..8663688e4 100644 --- a/ILSpy/ViewModels/PaneModel.cs +++ b/ILSpy/ViewModels/PaneModel.cs @@ -18,8 +18,11 @@ using System; using System.ComponentModel; +using System.Windows; using System.Windows.Input; +using ICSharpCode.ILSpy.Docking; + using TomsToolbox.Wpf; namespace ICSharpCode.ILSpy.ViewModels @@ -57,93 +60,81 @@ namespace ICSharpCode.ILSpy.ViewModels } } - public PaneModel() - { - this.closeCommand = new CloseCommandImpl(this); - } + private bool isSelected; - private bool isSelected = false; public bool IsSelected { get => isSelected; - set { - if (isSelected != value) - { - isSelected = value; - OnPropertyChanged(nameof(IsSelected)); - } - } + set => SetProperty(ref isSelected, value); } - private bool isActive = false; + private bool isActive; + public bool IsActive { get => isActive; - set { - if (isActive != value) - { - isActive = value; - OnPropertyChanged(nameof(IsActive)); - } - } + set => SetProperty(ref isActive, value); } private bool isVisible; + public bool IsVisible { get { return isVisible; } set { - if (isVisible != value) + if (SetProperty(ref isVisible, value) && !value) { - isVisible = value; - OnPropertyChanged(nameof(IsVisible)); + // When the pane is hidden, it should no longer be marked as active, else it won't raise an event when it is activated again. + IsActive = false; } } } private bool isCloseable = true; + public bool IsCloseable { - get { return isCloseable; } - set { - if (isCloseable != value) - { - isCloseable = value; - OnPropertyChanged(nameof(IsCloseable)); - } - } + get => isCloseable; + set => SetProperty(ref isCloseable, value); } - private ICommand closeCommand; - public ICommand CloseCommand { - get { return closeCommand; } - set { - if (closeCommand != value) - { - closeCommand = value; - OnPropertyChanged(nameof(CloseCommand)); - } - } - } + public ICommand CloseCommand => new CloseCommandImpl(this); private string contentId; + public string ContentId { get => contentId; - set { - if (contentId != value) - { - contentId = value; - OnPropertyChanged(nameof(ContentId)); - } - } + set => SetProperty(ref contentId, value); } private string title; + public string Title { get => title; - set { - if (title != value) - { - title = value; - OnPropertyChanged(nameof(Title)); - } - } + set => SetProperty(ref title, value); + } + } + + public static class Pane + { + // Helper properties to enable binding state properties from the model to the view. + + public static readonly DependencyProperty IsActiveProperty = DependencyProperty.RegisterAttached( + "IsActive", typeof(bool), typeof(Pane), new FrameworkPropertyMetadata(default(bool))); + public static void SetIsActive(DependencyObject element, bool value) + { + element.SetValue(IsActiveProperty, value); + } + public static bool GetIsActive(DependencyObject element) + { + return (bool)element.GetValue(IsActiveProperty); + } + + public static readonly DependencyProperty IsVisibleProperty = DependencyProperty.RegisterAttached( + "IsVisible", typeof(bool), typeof(Pane), new FrameworkPropertyMetadata(default(bool))); + public static void SetIsVisible(DependencyObject element, bool value) + { + element.SetValue(IsVisibleProperty, value); + } + public static bool GetIsVisible(DependencyObject element) + { + return (bool)element.GetValue(IsVisibleProperty); } } } From 524ab6035f9c02b5b310607980e6d30a70173328 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 25 Aug 2024 10:10:37 +0200 Subject: [PATCH 35/35] Fix #3226: missing assemblyFileName parameter in XmlDocLoader.FindXmlDocumentation --- .../Documentation/XmlDocLoader.cs | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/ICSharpCode.Decompiler/Documentation/XmlDocLoader.cs b/ICSharpCode.Decompiler/Documentation/XmlDocLoader.cs index ba2b1d95b..3d4c9ce6c 100644 --- a/ICSharpCode.Decompiler/Documentation/XmlDocLoader.cs +++ b/ICSharpCode.Decompiler/Documentation/XmlDocLoader.cs @@ -91,14 +91,25 @@ namespace ICSharpCode.Decompiler.Documentation fileName = LookupLocalizedXmlDoc(Path.Combine(frameworkPath, "v1.1.4322", assemblyFileName)); break; case TargetRuntime.Net_2_0: - fileName = LookupLocalizedXmlDoc(Path.Combine(frameworkPath, "v2.0.50727", assemblyFileName)) - ?? LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, "v3.5")) - ?? LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, "v3.0")) - ?? LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, @".NETFramework\v3.5\Profile\Client")); + fileName = LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, "v3.5", assemblyFileName)) + ?? LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, @".NETFramework\v3.5\Profile\Client", assemblyFileName)) + ?? LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, "v3.0", assemblyFileName)) + ?? LookupLocalizedXmlDoc(Path.Combine(frameworkPath, "v2.0.50727", assemblyFileName)); break; case TargetRuntime.Net_4_0: default: - fileName = LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, @".NETFramework\v4.0", assemblyFileName)) + fileName = LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, @".NETFramework\v4.8.1", assemblyFileName)) + ?? LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, @".NETFramework\v4.8", assemblyFileName)) + ?? LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, @".NETFramework\v4.7.2", assemblyFileName)) + ?? LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, @".NETFramework\v4.7.1", assemblyFileName)) + ?? LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, @".NETFramework\v4.7", assemblyFileName)) + ?? LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, @".NETFramework\v4.6.2", assemblyFileName)) + ?? LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, @".NETFramework\v4.6.1", assemblyFileName)) + ?? LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, @".NETFramework\v4.6", assemblyFileName)) + ?? LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, @".NETFramework\v4.5.2", assemblyFileName)) + ?? LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, @".NETFramework\v4.5.1", assemblyFileName)) + ?? LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, @".NETFramework\v4.5", assemblyFileName)) + ?? LookupLocalizedXmlDoc(Path.Combine(referenceAssembliesPath, @".NETFramework\v4.0", assemblyFileName)) ?? LookupLocalizedXmlDoc(Path.Combine(frameworkPath, "v4.0.30319", assemblyFileName)); break; }