Skip to content

Commit 9cf5173

Browse files
committed
show base members
1 parent 2004fcb commit 9cf5173

7 files changed

Lines changed: 105 additions & 14 deletions

File tree

ILSpy/AssemblyTree/AssemblyListPane.axaml.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public AssemblyListPane()
8080

8181
void OnLanguageSettingsChanged(object? sender, PropertyChangedEventArgs e)
8282
{
83-
if (e.PropertyName != nameof(LanguageSettings.ShowApiLevel))
83+
if (e.PropertyName != nameof(LanguageSettings.ShowApiLevel) && e.PropertyName != nameof(LanguageSettings.ShowBaseApi))
8484
return;
8585
// Re-apply the API-level filter in place; the flattener drops anything newly hidden.
8686
if (DataContext is AssemblyTreeModel { Root: ILSpyTreeNode root })

ILSpy/LanguageSettings.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public LanguageSettings(XElement element, ISettingsSection parent)
3636
{
3737
Parent = parent;
3838
showApiLevel = (ApiVisibility?)(int?)element.Element("ShowAPILevel") ?? ApiVisibility.PublicAndInternal;
39+
showBaseApi = (bool?)element.Element("ShowBaseAPI") ?? false;
3940
languageId = (string?)element.Element("Language");
4041
languageVersionId = (string?)element.Element("LanguageVersion");
4142
}
@@ -47,6 +48,7 @@ public XElement SaveAsXml()
4748
return new XElement(
4849
"FilterSettings",
4950
new XElement("ShowAPILevel", (int)ShowApiLevel),
51+
new XElement("ShowBaseAPI", ShowBaseApi),
5052
new XElement("Language", LanguageId),
5153
new XElement("LanguageVersion", LanguageVersionId));
5254
}
@@ -80,6 +82,18 @@ public bool ApiVisAll {
8082
set { if (value) ShowApiLevel = ApiVisibility.All; }
8183
}
8284

85+
bool showBaseApi;
86+
87+
public bool ShowBaseApi {
88+
get => showBaseApi;
89+
set {
90+
if (SetProperty(ref showBaseApi, value))
91+
{
92+
OnPropertyChanged(nameof(ShowBaseApi));
93+
}
94+
}
95+
}
96+
8397
[ObservableProperty]
8498
string? languageId;
8599

ILSpy/Properties/Resources.Designer.cs

Lines changed: 11 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ILSpy/Properties/Resources.resx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1036,8 +1036,11 @@ Do you want to continue?</value>
10361036
<data name="Show_allTypesAndMembers" xml:space="preserve">
10371037
<value>Show _all types and members</value>
10381038
</data>
1039+
<data name="Show_baseMembers" xml:space="preserve">
1040+
<value>Show _base members</value>
1041+
</data>
10391042
<data name="Show_internalTypesMembers" xml:space="preserve">
1040-
<value>Show public, private and internal</value>
1043+
<value>Show public, private and _internal</value>
10411044
</data>
10421045
<data name="Show_publiconlyTypesMembers" xml:space="preserve">
10431046
<value>Show only _public types and members</value>

ILSpy/TreeNodes/ILSpyTreeNode.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,18 @@ protected static LanguageSettings? CurrentLanguageSettings {
8888
/// "Show metadata tokens". Mirrors WPF's <c>ILSpyTreeNode.GetSuffixString</c>; format
8989
/// matches byte-for-byte so cross-tool grep on token strings keeps working.
9090
/// </summary>
91-
protected static string GetSuffixString(IMember member) => GetSuffixString(member.MetadataToken);
91+
protected string GetSuffixString(IMember member) => GetSuffixString(member.MetadataToken) + GetTypeSuffixString(member);
92+
93+
private string GetTypeSuffixString(IMember member)
94+
{
95+
if (Parent is not TypeTreeNode typeNode)
96+
return string.Empty;
97+
98+
if (member.DeclaringTypeDefinition?.MetadataToken == typeNode.Handle)
99+
return string.Empty;
100+
101+
return $" ({member.DeclaringType.Name})";
102+
}
92103

93104
protected static string GetSuffixString(EntityHandle handle)
94105
{

ILSpy/TreeNodes/TypeTreeNode.cs

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
// DEALINGS IN THE SOFTWARE.
1818

1919
using System;
20+
using System.Collections.Generic;
2021
using System.Linq;
2122
using System.Reflection.Metadata;
2223

@@ -25,7 +26,6 @@
2526
using ICSharpCode.Decompiler.Output;
2627
using ICSharpCode.Decompiler.TypeSystem;
2728
using ICSharpCode.ILSpyX;
28-
2929
using ICSharpCode.ILSpy;
3030
using ICSharpCode.ILSpy.AppEnv;
3131
using ICSharpCode.ILSpy.AssemblyTree;
@@ -38,6 +38,8 @@ public sealed class TypeTreeNode : ILSpyTreeNode, IMemberTreeNode
3838
readonly TypeDefinitionHandle handle;
3939
readonly MetadataFile module;
4040

41+
bool? loadedInheritedMembers;
42+
4143
public TypeDefinitionHandle Handle => handle;
4244
public MetadataFile Module => module;
4345

@@ -97,6 +99,11 @@ public override void Decompile(Language language, ITextOutput output, Decompilat
9799

98100
public override FilterResult Filter(LanguageSettings settings)
99101
{
102+
if (loadedInheritedMembers != settings.ShowBaseApi && IsVisible)
103+
{
104+
ReloadChildren();
105+
}
106+
100107
if (settings.ShowApiLevel == ApiVisibility.PublicOnly && !IsPublicAPI)
101108
return FilterResult.Hidden;
102109
var typeDef = ResolveTypeDefinition();
@@ -163,7 +170,14 @@ protected override void LoadChildren()
163170
if (assemblyList != null && CanHaveDerivedTypes(typeDef))
164171
Children.Add(new DerivedTypesTreeNode(assemblyList, typeDef));
165172

166-
foreach (var nestedType in typeDef.NestedTypes
173+
loadedInheritedMembers = CurrentLanguageSettings?.ShowBaseApi;
174+
IEnumerable<ITypeDefinition> nestedTypes = GetMembers(typeDef, t => t.NestedTypes, loadedInheritedMembers);
175+
IEnumerable<IField> fields = GetMembers(typeDef, t => t.Fields, loadedInheritedMembers);
176+
IEnumerable<IProperty> properties = GetMembers(typeDef, t => t.Properties, loadedInheritedMembers);
177+
IEnumerable<IEvent> events = GetMembers(typeDef, t => t.Events, loadedInheritedMembers);
178+
IEnumerable<IMethod> methods = GetMembers(typeDef, t => t.Methods, loadedInheritedMembers);
179+
180+
foreach (var nestedType in nestedTypes
167181
.OrderBy(t => t.Name, NaturalStringComparer.Instance))
168182
{
169183
Children.Add(new TypeTreeNode((TypeDefinitionHandle)nestedType.MetadataToken, module));
@@ -184,24 +198,40 @@ protected override void LoadChildren()
184198
}
185199

186200
// Enums look more useful in declaration order than alphabetical.
187-
var fields = typeDef.Kind == TypeKind.Enum
188-
? typeDef.Fields
189-
: typeDef.Fields.OrderBy(f => f.Name, NaturalStringComparer.Instance);
201+
if (typeDef.Kind != TypeKind.Enum)
202+
fields = fields.OrderBy(f => f.Name, NaturalStringComparer.Instance);
203+
190204
foreach (var field in fields)
191205
Children.Add(new FieldTreeNode(field));
192206

193-
foreach (var prop in typeDef.Properties.OrderBy(p => p.Name, NaturalStringComparer.Instance))
207+
foreach (var prop in properties.OrderBy(p => p.Name, NaturalStringComparer.Instance))
194208
Children.Add(new PropertyTreeNode(prop));
195209

196-
foreach (var ev in typeDef.Events.OrderBy(e => e.Name, NaturalStringComparer.Instance))
210+
foreach (var ev in events.OrderBy(e => e.Name, NaturalStringComparer.Instance))
197211
Children.Add(new EventTreeNode(ev));
198212

199-
foreach (var method in typeDef.Methods.OrderBy(m => m.Name, NaturalStringComparer.Instance))
213+
foreach (var method in methods.OrderBy(m => m.Name, NaturalStringComparer.Instance))
200214
{
201-
if (method.MetadataToken.IsNil || method.IsAccessor)
215+
if (method.MetadataToken.IsNil || method.IsAccessor)
202216
continue;
203217
Children.Add(new MethodTreeNode(method));
204218
}
205219
}
220+
221+
private IEnumerable<TMember> GetMembers<TMember>(ITypeDefinition type, Func<ITypeDefinition, IEnumerable<TMember>> selector, bool? includeInherited)
222+
{
223+
IEnumerable<TMember> allMembers = selector(type);
224+
if (includeInherited == true)
225+
{
226+
var typeSystem = new DecompilerTypeSystem(module, module.GetAssemblyResolver(),
227+
TypeSystemOptions.Default | TypeSystemOptions.Uncached);
228+
229+
if (typeSystem.MainModule.ResolveEntity(handle) is ITypeDefinition resolvedType)
230+
foreach (var baseType in resolvedType.GetNonInterfaceBaseTypes().Reverse().Select(t => t.GetDefinition()))
231+
if (baseType != null && baseType != type)
232+
allMembers = allMembers.Concat(selector(baseType));
233+
}
234+
return allMembers;
235+
}
206236
}
207237
}

ILSpy/Views/MainMenu.axaml.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ static void PopulateViewMenu(NativeMenu view, SessionSettings session, SetThemeC
151151

152152
view.Items.Add(new NativeMenuItemSeparator());
153153

154+
view.Items.Add(MakeCheckBox(Resources.Show_baseMembers, languageSettings, nameof(LanguageSettings.ShowBaseApi)));
155+
156+
view.Items.Add(new NativeMenuItemSeparator());
157+
154158
var theme = new NativeMenuItem {
155159
Header = Resources.Theme,
156160
Menu = new NativeMenu(),
@@ -180,6 +184,26 @@ static NativeMenuItem MakeRadio(string header, object source, string path)
180184
return item;
181185
}
182186

187+
static NativeMenuItem MakeCheckBox(string header, object source, string path)
188+
{
189+
var item = new NativeMenuItem {
190+
Header = header,
191+
ToggleType = MenuItemToggleType.CheckBox,
192+
};
193+
// IsChecked is purely for *displaying* the checkmark (OneWay). The write
194+
// path lives in Command, because Avalonia's macOS NativeMenu bridge maps
195+
// NativeMenuItem -> NSMenuItem only when Command != null; without it the
196+
// item gets greyed out by NSMenuValidation and no click ever reaches the
197+
// IsChecked binding.
198+
item.Bind(NativeMenuItem.IsCheckedProperty, new Binding(path) {
199+
Source = source,
200+
Mode = BindingMode.OneWay,
201+
});
202+
var property = source.GetType().GetProperty(path);
203+
item.Command = new RelayCommand(() => property?.SetValue(source, property?.GetValue(source) is true ? false : true));
204+
return item;
205+
}
206+
183207
static void PopulateThemeSubmenu(NativeMenu theme, SessionSettings session, SetThemeCommand setThemeCommand)
184208
{
185209
foreach (var themeName in ThemeManager.AllThemes)

0 commit comments

Comments
 (0)