Skip to content

Commit 5b624a4

Browse files
committed
Overhaul the code explorer.
1 parent bae177d commit 5b624a4

File tree

53 files changed

+3772
-1232
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+3772
-1232
lines changed

Rubberduck.Core/CodeAnalysis/CodeMetrics/CodeMetricsViewModel.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,8 @@ public ObservableCollection<ICodeMetricResult> Metrics
169169
{
170170
get
171171
{
172-
var results = resultsByDeclaration?.FirstOrDefault(f => f.Key == SelectedItem.GetSelectedDeclaration());
173-
return !results.HasValue || results.Value.Value == null ? new ObservableCollection<ICodeMetricResult>() : new ObservableCollection<ICodeMetricResult>(results.Value.Value);
172+
var results = resultsByDeclaration?.FirstOrDefault(f => ReferenceEquals(f.Key, SelectedItem.Declaration));
173+
return results?.Value == null ? new ObservableCollection<ICodeMetricResult>() : new ObservableCollection<ICodeMetricResult>(results.Value.Value);
174174
}
175175
}
176176

Rubberduck.Core/Navigation/CodeExplorer/CodeExplorerComponentViewModel.cs

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,11 @@
1313

1414
namespace Rubberduck.Navigation.CodeExplorer
1515
{
16-
public class CodeExplorerComponentViewModel : CodeExplorerItemViewModel, ICodeExplorerDeclarationViewModel
16+
public class CodeExplorerComponentViewModel : CodeExplorerItemViewModel
1717
{
18-
public Declaration Declaration { get; }
19-
2018
public override CodeExplorerItemViewModel Parent { get; }
2119

22-
private static readonly DeclarationType[] MemberTypes =
20+
public static readonly DeclarationType[] MemberTypes =
2321
{
2422
DeclarationType.Constant,
2523
DeclarationType.Enumeration,
@@ -32,18 +30,13 @@ public class CodeExplorerComponentViewModel : CodeExplorerItemViewModel, ICodeEx
3230
DeclarationType.PropertyLet,
3331
DeclarationType.PropertySet,
3432
DeclarationType.UserDefinedType,
35-
DeclarationType.Variable,
33+
DeclarationType.Variable
3634
};
3735

38-
private readonly IProjectsProvider _projectsProvider;
39-
private readonly IVBE _vbe;
40-
41-
public CodeExplorerComponentViewModel(CodeExplorerItemViewModel parent, Declaration declaration, IEnumerable<Declaration> declarations, IProjectsProvider projectsProvider, IVBE vbe)
36+
public CodeExplorerComponentViewModel(CodeExplorerItemViewModel parent, Declaration declaration, IEnumerable<Declaration> declarations, IProjectsProvider projectsProvider, IVBE vbe)
37+
: base(declaration)
4238
{
4339
Parent = parent;
44-
Declaration = declaration;
45-
_projectsProvider = projectsProvider;
46-
_vbe = vbe;
4740

4841
_icon = Icons.ContainsKey(DeclarationType)
4942
? Icons[DeclarationType]
@@ -69,7 +62,7 @@ public CodeExplorerComponentViewModel(CodeExplorerItemViewModel parent, Declarat
6962
case ComponentType.Document:
7063
var parenthesizedName = string.Empty;
7164
var state = DocumentState.Inaccessible;
72-
using (var app = _vbe.HostApplication())
65+
using (var app = vbe.HostApplication())
7366
{
7467
if (app != null)
7568
{
@@ -79,15 +72,18 @@ public CodeExplorerComponentViewModel(CodeExplorerItemViewModel parent, Declarat
7972
}
8073
}
8174

82-
if (state == DocumentState.DesignView && ContainsBuiltinDocumentPropertiesProperty())
75+
if (state == DocumentState.DesignView && ContainsBuiltinDocumentPropertiesProperty(projectsProvider))
8376
{
8477
CodeExplorerItemViewModel node = this;
8578
while (node.Parent != null)
8679
{
8780
node = node.Parent;
8881
}
8982

90-
((CodeExplorerProjectViewModel) node).SetParenthesizedName(parenthesizedName);
83+
if (node is CodeExplorerProjectViewModel projectNode)
84+
{
85+
projectNode.SetParenthesizedName(parenthesizedName);
86+
}
9187
}
9288
else
9389
{
@@ -118,9 +114,9 @@ public CodeExplorerComponentViewModel(CodeExplorerItemViewModel parent, Declarat
118114
}
119115
}
120116

121-
private bool ContainsBuiltinDocumentPropertiesProperty()
117+
private bool ContainsBuiltinDocumentPropertiesProperty(IProjectsProvider projectsProvider)
122118
{
123-
var component = _projectsProvider.Component(Declaration.QualifiedName.QualifiedModuleName);
119+
var component = projectsProvider.Component(Declaration.QualifiedName.QualifiedModuleName);
124120
using (var properties = component.Properties)
125121
{
126122
foreach (var property in properties)
@@ -152,8 +148,10 @@ public bool IsErrorState
152148
}
153149

154150
OnPropertyChanged();
155-
OnPropertyChanged("CollapsedIcon");
151+
// ReSharper disable ExplicitCallerInfoArgument
152+
OnPropertyChanged("CollapsedIcon");
156153
OnPropertyChanged("ExpandedIcon");
154+
// ReSharper restore ExplicitCallerInfoArgument
157155
}
158156
}
159157

Rubberduck.Core/Navigation/CodeExplorer/CodeExplorerCustomFolderViewModel.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,27 @@ public class CodeExplorerCustomFolderViewModel : CodeExplorerItemViewModel
1616
DeclarationType.ClassModule,
1717
DeclarationType.Document,
1818
DeclarationType.ProceduralModule,
19-
DeclarationType.UserForm,
19+
DeclarationType.UserForm
2020
};
2121

2222
private readonly IProjectsProvider _projectsProvider;
2323
private readonly IVBE _vbe;
2424

25-
public CodeExplorerCustomFolderViewModel(CodeExplorerItemViewModel parent, string name, string fullPath, IProjectsProvider projectsProvider, IVBE vbe)
25+
public CodeExplorerCustomFolderViewModel(
26+
CodeExplorerItemViewModel parent,
27+
string name,
28+
string fullPath,
29+
IProjectsProvider projectsProvider,
30+
IVBE vbe,
31+
Declaration declaration = null) : base(declaration ?? parent?.Declaration)
2632
{
2733
_parent = parent;
2834
_projectsProvider = projectsProvider;
2935
_vbe = vbe;
3036

3137
FullPath = fullPath;
3238
Name = name.Replace("\"", string.Empty);
33-
FolderAttribute = string.Format("@Folder(\"{0}\")", fullPath.Replace("\"", string.Empty));
39+
FolderAttribute = $"@Folder(\"{fullPath.Replace("\"", string.Empty)}\")";
3440

3541
CollapsedIcon = GetImageSource(Resources.CodeExplorer.CodeExplorerUI.FolderClosed);
3642
ExpandedIcon = GetImageSource(Resources.CodeExplorer.CodeExplorerUI.FolderOpen);
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Rubberduck.Parsing.Symbols;
4+
using Rubberduck.VBEditor.SafeComWrappers;
5+
6+
namespace Rubberduck.Navigation.CodeExplorer
7+
{
8+
public class CompareByName : Comparer<CodeExplorerItemViewModel>
9+
{
10+
public override int Compare(CodeExplorerItemViewModel x, CodeExplorerItemViewModel y)
11+
{
12+
if (x == y)
13+
{
14+
return 0;
15+
}
16+
17+
var nodeComparison = new CompareByNodeType().Compare(x, y);
18+
19+
return nodeComparison != 0
20+
? nodeComparison
21+
: string.Compare(x?.NameWithSignature, y?.NameWithSignature, StringComparison.OrdinalIgnoreCase);
22+
}
23+
}
24+
25+
public class CompareByType : Comparer<CodeExplorerItemViewModel>
26+
{
27+
private static readonly Dictionary<DeclarationType, int> SortOrder = new Dictionary<DeclarationType, int>
28+
{
29+
// Some DeclarationTypes we want to treat the same, like Subs and Functions,
30+
// or Property Gets, Lets, and Sets.
31+
// Give them the same number.
32+
{DeclarationType.LibraryFunction, 0},
33+
{DeclarationType.LibraryProcedure, 0},
34+
{DeclarationType.UserDefinedType, 1},
35+
{DeclarationType.Enumeration, 2},
36+
{DeclarationType.Event, 3},
37+
{DeclarationType.Constant, 4},
38+
{DeclarationType.Variable, 5},
39+
{DeclarationType.PropertyGet, 6},
40+
{DeclarationType.PropertyLet, 6},
41+
{DeclarationType.PropertySet, 6},
42+
{DeclarationType.Function, 7},
43+
{DeclarationType.Procedure, 7}
44+
};
45+
46+
public override int Compare(CodeExplorerItemViewModel x, CodeExplorerItemViewModel y)
47+
{
48+
if (x == y)
49+
{
50+
return 0;
51+
}
52+
53+
var nodeComparison = new CompareByNodeType().Compare(x, y);
54+
if (nodeComparison != 0)
55+
{
56+
return nodeComparison;
57+
}
58+
59+
//null sorts last.
60+
if (x?.Declaration == null)
61+
{
62+
return 1;
63+
}
64+
65+
if (y?.Declaration == null)
66+
{
67+
return -1;
68+
}
69+
70+
// keep separate types separate
71+
if (x.Declaration.DeclarationType != y.Declaration.DeclarationType)
72+
{
73+
if (SortOrder.TryGetValue(x.Declaration.DeclarationType, out var xValue) &&
74+
SortOrder.TryGetValue(y.Declaration.DeclarationType, out var yValue))
75+
{
76+
return xValue.CompareTo(yValue);
77+
}
78+
}
79+
80+
// The Tree shows Public and Private Subs/Functions with a separate icon.
81+
// But Public and Implicit Subs/Functions appear the same, so treat Implicits like Publics.
82+
var xNodeAcc = x.Declaration.Accessibility == Accessibility.Implicit ? Accessibility.Public : x.Declaration.Accessibility;
83+
var yNodeAcc = y.Declaration.Accessibility == Accessibility.Implicit ? Accessibility.Public : y.Declaration.Accessibility;
84+
85+
if (xNodeAcc != yNodeAcc)
86+
{
87+
// These are reversed because Accessibility is ordered lowest to highest.
88+
return yNodeAcc.CompareTo(xNodeAcc);
89+
}
90+
91+
if (x.ExpandedIcon != y.ExpandedIcon)
92+
{
93+
// ReSharper disable PossibleInvalidOperationException - this will have a QualifiedSelection
94+
var xQmn = x.QualifiedSelection.Value.QualifiedName;
95+
var yQmn = y.QualifiedSelection.Value.QualifiedName;
96+
97+
if (xQmn.ComponentType == ComponentType.Document ^ yQmn.ComponentType == ComponentType.Document)
98+
{
99+
return xQmn.ComponentType == ComponentType.Document ? -1 : 1;
100+
}
101+
}
102+
103+
return 0;
104+
}
105+
}
106+
107+
public class CompareBySelection : Comparer<CodeExplorerItemViewModel>
108+
{
109+
public override int Compare(CodeExplorerItemViewModel x, CodeExplorerItemViewModel y)
110+
{
111+
if (x == y)
112+
{
113+
return 0;
114+
}
115+
116+
var nodeComparison = new CompareByNodeType().Compare(x, y);
117+
if (nodeComparison != 0)
118+
{
119+
return nodeComparison;
120+
}
121+
122+
// ReSharper disable PossibleNullReferenceException - tested in CompareByNodeType() above
123+
if (!x.QualifiedSelection.HasValue && !y.QualifiedSelection.HasValue)
124+
125+
{
126+
return 0;
127+
}
128+
129+
if (x.QualifiedSelection.HasValue ^ y.QualifiedSelection.HasValue)
130+
{
131+
return x.QualifiedSelection.HasValue ? -1 : 1;
132+
}
133+
134+
if (x.QualifiedSelection.Value.Selection.StartLine == y.QualifiedSelection.Value.Selection.StartLine)
135+
{
136+
return 0;
137+
}
138+
139+
return x.QualifiedSelection.Value.Selection.StartLine < y.QualifiedSelection.Value.Selection.StartLine ? -1 : 1;
140+
// ReSharper restore PossibleNullReferenceException
141+
}
142+
}
143+
144+
public class CompareByNodeType : Comparer<CodeExplorerItemViewModel>
145+
{
146+
public override int Compare(CodeExplorerItemViewModel x, CodeExplorerItemViewModel y)
147+
{
148+
if (x == y)
149+
{
150+
return 0;
151+
}
152+
153+
//null sorts last.
154+
if (x == null)
155+
{
156+
return 1;
157+
}
158+
159+
if (y == null)
160+
{
161+
return -1;
162+
}
163+
164+
// references come first
165+
if (x is CodeExplorerReferenceFolderViewModel ^
166+
y is CodeExplorerReferenceFolderViewModel)
167+
{
168+
return x is CodeExplorerReferenceFolderViewModel ? -1 : 1;
169+
}
170+
171+
// references always sort by priority
172+
if (x is CodeExplorerReferenceViewModel first &&
173+
y is CodeExplorerReferenceViewModel second)
174+
{
175+
return first.Priority > second.Priority ? 1 : -1;
176+
}
177+
178+
// folders come next
179+
if (x is CodeExplorerCustomFolderViewModel ^
180+
y is CodeExplorerCustomFolderViewModel)
181+
{
182+
return x is CodeExplorerCustomFolderViewModel ? -1 : 1;
183+
}
184+
185+
// folders are always sorted by name
186+
if (x is CodeExplorerCustomFolderViewModel)
187+
{
188+
return string.CompareOrdinal(x.NameWithSignature, y.NameWithSignature);
189+
}
190+
191+
return 0;
192+
}
193+
}
194+
195+
}

0 commit comments

Comments
 (0)