Browse Source

WIP

feature/api-diff
Siegfried Pammer 2 months ago
parent
commit
a640e4a712
  1. 10
      ILSpy/Commands/CompareContextMenuEntry.cs
  2. 108
      ILSpy/ViewModels/CompareViewModel.cs
  3. 9
      ILSpy/Views/CompareView.xaml

10
ILSpy/Commands/CompareContextMenuEntry.cs

@ -17,6 +17,7 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System.Composition; using System.Composition;
using System.Threading.Tasks;
using ICSharpCode.ILSpy.AssemblyTree; using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpy.Docking; using ICSharpCode.ILSpy.Docking;
@ -37,12 +38,19 @@ namespace ICSharpCode.ILSpy
var tabPage = dockWorkspace.AddTabPage(); var tabPage = dockWorkspace.AddTabPage();
tabPage.ShowTextView(t => t.RunWithCancellation(token => Task.Run(DoCompare, token), $"Comparing {left.Text} - {right.Text}").Then(vm => {
tabPage.Title = $"Compare {left.Text} - {right.Text}"; tabPage.Title = $"Compare {left.Text} - {right.Text}";
tabPage.SupportsLanguageSwitching = false; tabPage.SupportsLanguageSwitching = false;
tabPage.FrozenContent = true; tabPage.FrozenContent = true;
var compareView = new CompareView(); var compareView = new CompareView();
compareView.DataContext = new CompareViewModel(assemblyTreeModel, left, right); compareView.DataContext = vm;
tabPage.Content = compareView; tabPage.Content = compareView;
}));
CompareViewModel DoCompare()
{
return new CompareViewModel(assemblyTreeModel, left, right);
}
} }
public bool IsEnabled(TextViewContext context) public bool IsEnabled(TextViewContext context)

108
ILSpy/ViewModels/CompareViewModel.cs

@ -10,6 +10,8 @@ using ICSharpCode.ILSpyX;
using TomsToolbox.Wpf; using TomsToolbox.Wpf;
#nullable enable
namespace ICSharpCode.ILSpy.ViewModels namespace ICSharpCode.ILSpy.ViewModels
{ {
using System.Linq; using System.Linq;
@ -142,7 +144,7 @@ namespace ICSharpCode.ILSpy.ViewModels
Dictionary<TypeDefinitionHandle, Entry> typeEntries = new(); Dictionary<TypeDefinitionHandle, Entry> typeEntries = new();
Dictionary<string, Entry> namespaceEntries = new(StringComparer.Ordinal); Dictionary<string, Entry> namespaceEntries = new(StringComparer.Ordinal);
Entry root = new Entry { Entity = null!, Signature = module.FullAssemblyName }; Entry root = new Entry { Entity = module, Signature = module.FullAssemblyName };
// typeEntries need a different signature: must include list of base types // typeEntries need a different signature: must include list of base types
@ -190,7 +192,7 @@ namespace ICSharpCode.ILSpy.ViewModels
{ {
if (!namespaceEntries.TryGetValue(typeDef.Namespace, out var nsEntry)) if (!namespaceEntries.TryGetValue(typeDef.Namespace, out var nsEntry))
{ {
namespaceEntries[typeDef.Namespace] = nsEntry = new Entry { Parent = root, Signature = typeDef.Namespace, Entity = null! }; namespaceEntries[typeDef.Namespace] = nsEntry = new Entry { Parent = root, Signature = typeDef.Namespace, Entity = ResolveNamespace(typeDef.Namespace, typeDef.ParentModule)! };
root.Children ??= new(); root.Children ??= new();
root.Children.Add(nsEntry); root.Children.Add(nsEntry);
} }
@ -242,6 +244,27 @@ namespace ICSharpCode.ILSpy.ViewModels
} }
return (results, root); return (results, root);
INamespace? ResolveNamespace(string namespaceName, IModule module)
{
INamespace current = module.RootNamespace;
string[] parts = namespaceName.Split('.');
for (int i = 0; i < parts.Length; i++)
{
if (i == 0 && string.IsNullOrEmpty(parts[i]))
{
continue;
}
var next = current.GetChildNamespace(parts[i]);
if (next != null)
current = next;
else
return null;
}
return current;
}
} }
List<(Entry? Left, Entry? Right)> CalculateDiff(List<Entry> left, List<Entry> right) List<(Entry? Left, Entry? Right)> CalculateDiff(List<Entry> left, List<Entry> right)
@ -279,7 +302,7 @@ namespace ICSharpCode.ILSpy.ViewModels
results.Add((item, other)); results.Add((item, other));
if (other == null) if (other == null)
{ {
item.Kind = DiffKind.Remove; SetKind(item, DiffKind.Remove);
} }
} }
} }
@ -287,7 +310,7 @@ namespace ICSharpCode.ILSpy.ViewModels
{ {
foreach (var item in items) foreach (var item in items)
{ {
item.Kind = DiffKind.Remove; SetKind(item, DiffKind.Remove);
results.Add((item, null)); results.Add((item, null));
} }
} }
@ -302,7 +325,7 @@ namespace ICSharpCode.ILSpy.ViewModels
if (!leftEntries.Any(_ => EntryComparer.Instance.Equals(_, item))) if (!leftEntries.Any(_ => EntryComparer.Instance.Equals(_, item)))
{ {
results.Add((null, item)); results.Add((null, item));
item.Kind = DiffKind.Add; SetKind(item, DiffKind.Add);
} }
} }
} }
@ -310,27 +333,40 @@ namespace ICSharpCode.ILSpy.ViewModels
{ {
foreach (var item in items) foreach (var item in items)
{ {
item.Kind = DiffKind.Add; SetKind(item, DiffKind.Add);
results.Add((null, item)); results.Add((null, item));
} }
} }
} }
return results; return results;
static void SetKind(Entry item, DiffKind kind)
{
if (item.Children?.Count > 0)
{
foreach (var child in item.Children)
{
SetKind(child, kind);
}
}
else
{
item.Kind = kind;
}
}
} }
} }
[DebuggerDisplay($"{{{nameof(GetDebuggerDisplay)}(),nq}}")] [DebuggerDisplay($"{{{nameof(GetDebuggerDisplay)}(),nq}}")]
public class Entry public class Entry
{ {
private DiffKind kind = DiffKind.None; private DiffKind? kind;
public DiffKind Kind { public DiffKind RecursiveKind {
get { get {
if (Children == null || Children.Count == 0) if (kind != null)
{ return kind.Value;
return kind;
}
int addCount = 0, removeCount = 0, updateCount = 0; int addCount = 0, removeCount = 0, updateCount = 0;
@ -358,15 +394,11 @@ namespace ICSharpCode.ILSpy.ViewModels
return DiffKind.Update; return DiffKind.Update;
return DiffKind.None; return DiffKind.None;
} }
set {
if (Children == null || Children.Count == 0)
{
kind = value;
}
}
} }
public DiffKind Kind { get; set; }
public required string Signature { get; init; } public required string Signature { get; init; }
public required IEntity Entity { get; init; } public required ISymbol Entity { get; init; }
public Entry? Parent { get; set; } public Entry? Parent { get; set; }
public List<Entry>? Children { get; set; } public List<Entry>? Children { get; set; }
@ -421,7 +453,29 @@ namespace ICSharpCode.ILSpy.ViewModels
} }
} }
public override object Text => entry.Signature; public override object Text {
get {
switch (entry.Entity)
{
case ITypeDefinition t:
return this.Language.TypeToString(t, includeNamespace: false) + GetSuffixString(t.MetadataToken);
case IMethod m:
return this.Language.MethodToString(m, false, false, false) + GetSuffixString(m.MetadataToken);
case IField f:
return this.Language.FieldToString(f, false, false, false) + GetSuffixString(f.MetadataToken);
case IProperty p:
return this.Language.PropertyToString(p, false, false, false) + GetSuffixString(p.MetadataToken);
case IEvent e:
return this.Language.EventToString(e, false, false, false) + GetSuffixString(e.MetadataToken);
case INamespace n:
return n.FullName;
case IModule m:
return m.FullAssemblyName;
default:
return entry.Signature;
}
}
}
public override object Icon { public override object Icon {
get { get {
@ -437,16 +491,28 @@ namespace ICSharpCode.ILSpy.ViewModels
return PropertyTreeNode.GetIcon(p); return PropertyTreeNode.GetIcon(p);
case IEvent e: case IEvent e:
return EventTreeNode.GetIcon(e); return EventTreeNode.GetIcon(e);
case INamespace n:
return Images.Namespace;
case IModule m:
return Images.Assembly;
default: default:
throw new NotSupportedException(); throw new NotSupportedException();
} }
} }
} }
public DiffKind Difference => entry.Kind;
public DiffKind RecursiveKind => entry.RecursiveKind;
public DiffKind Kind => entry.Kind;
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{ {
} }
//public override FilterResult Filter(LanguageSettings settings)
//{
// return RecursiveKind != DiffKind.None ? FilterResult.Match : FilterResult.Hidden;
//}
} }
} }

9
ILSpy/Views/CompareView.xaml

@ -30,15 +30,6 @@
</Border> </Border>
</Border> </Border>
<ControlTemplate.Triggers> <ControlTemplate.Triggers>
<DataTrigger Binding="{Binding Difference}" Value="Update">
<Setter Property="Background" Value="LightBlue" />
</DataTrigger>
<DataTrigger Binding="{Binding Difference}" Value="Add">
<Setter Property="Background" Value="LightGreen" />
</DataTrigger>
<DataTrigger Binding="{Binding Difference}" Value="Remove">
<Setter Property="Background" Value="LightPink" />
</DataTrigger>
<DataTrigger Binding="{Binding IsPublicAPI}" Value="False"> <DataTrigger Binding="{Binding IsPublicAPI}" Value="False">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" /> <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
</DataTrigger> </DataTrigger>

Loading…
Cancel
Save