Skip to content

Commit 8c45d27

Browse files
committed
feat: Added the ShowAllTypes property to the TypeOptions attribute
This property allows showing all types defined in a project in the dropdown. Also the filtering of types was refactored to return IEnumerable instead of lists and arrays.
1 parent 1302a1c commit 8c45d27

File tree

6 files changed

+94
-159
lines changed

6 files changed

+94
-159
lines changed

Editor/Drawers/TypeDropdownDrawer.cs

Lines changed: 27 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
{
33
using System;
44
using System.Collections.Generic;
5+
using System.Linq;
56
using System.Reflection;
67
using SolidUtilities;
78
using UnityDropdown.Editor;
@@ -27,7 +28,7 @@ public TypeDropdownDrawer(Type selectedType, TypeOptionsAttribute attribute, Typ
2728

2829
public void Draw(Action<Type> onTypeSelected)
2930
{
30-
var dropdownItems = GetDropdownItems();
31+
var dropdownItems = GetDropdownItems().ToList();
3132
SelectItem(dropdownItems, _selectedType);
3233

3334
var dropdownMenu = new DropdownMenu<Type>(dropdownItems, onTypeSelected, ProjectSettings.SearchbarMinItemsCount, true, _attribute.ShowNoneElement);
@@ -38,34 +39,23 @@ public void Draw(Action<Type> onTypeSelected)
3839
dropdownMenu.ShowAsContext(_attribute.DropdownHeight);
3940
}
4041

41-
public DropdownItem<Type>[] GetDropdownItems()
42+
public IEnumerable<DropdownItem<Type>> GetDropdownItems()
4243
{
43-
var filteredTypes = GetFilteredTypes();
44-
var includedTypes = GetIncludedTypes();
45-
46-
return includedTypes.Length == 0 ? filteredTypes : MergeArrays(filteredTypes, includedTypes);
44+
return GetMainDropdownItems().Concat(GetIncludedTypes());
4745
}
4846

49-
private void SelectItem(DropdownItem<Type>[] dropdownItems, Type selectedType)
47+
private void SelectItem(List<DropdownItem<Type>> dropdownItems, Type selectedType)
5048
{
5149
if (selectedType == null)
5250
return;
5351

54-
var itemToSelect = Array.Find(dropdownItems, item => item.Value == selectedType);
52+
var itemToSelect = dropdownItems.Find(item => item.Value == selectedType);
5553

5654
if (itemToSelect != null)
5755
itemToSelect.IsSelected = true;
5856
}
5957

60-
private DropdownItem<Type>[] MergeArrays(DropdownItem<Type>[] filteredTypes, DropdownItem<Type>[] includedTypes)
61-
{
62-
var totalTypes = new DropdownItem<Type>[filteredTypes.Length + includedTypes.Length];
63-
filteredTypes.CopyTo(totalTypes, 0);
64-
includedTypes.CopyTo(totalTypes, filteredTypes.Length);
65-
return totalTypes;
66-
}
67-
68-
private DropdownItem<Type>[] GetIncludedTypes()
58+
private IEnumerable<DropdownItem<Type>> GetIncludedTypes()
6959
{
7060
if (_attribute.IncludeTypes == null)
7161
return Array.Empty<DropdownItem<Type>>();
@@ -94,52 +84,43 @@ private DropdownItem<Type> CreateItem(Type type, Grouping grouping, string searc
9484
return new DropdownItem<Type>(type, TypeNameFormatter.Format(type, searchName, grouping), searchName: searchName);
9585
}
9686

97-
private DropdownItem<Type>[] GetFilteredTypes()
87+
private IEnumerable<DropdownItem<Type>> GetMainDropdownItems()
9888
{
9989
bool containsMSCorLib = false;
90+
var assemblies = _attribute.ShowAllTypes ? TypeCollector.GetAllAssemblies() : GetTypeRelatedAssemblies(out containsMSCorLib);
91+
92+
if (_attribute.ShowAllTypes)
93+
containsMSCorLib = true;
10094

101-
var typeRelatedAssemblies = ProjectSettings.UseBuiltInNames
102-
? TypeCollector.GetAssembliesTypeHasAccessTo(_declaringType, out containsMSCorLib)
103-
: TypeCollector.GetAssembliesTypeHasAccessTo(_declaringType);
104-
105-
if (_attribute.IncludeAdditionalAssemblies != null)
106-
IncludeAdditionalAssemblies(typeRelatedAssemblies);
107-
108-
var filteredTypes = TypeCollector.GetFilteredTypesFromAssemblies(typeRelatedAssemblies, _attribute);
95+
var filteredTypes = TypeCollector.GetFilteredTypesFromAssemblies(assemblies, _attribute);
10996

11097
bool replaceBuiltInNames = ProjectSettings.UseBuiltInNames && containsMSCorLib;
11198

112-
int filteredTypesLength = filteredTypes.Count;
113-
114-
var typeItems = new DropdownItem<Type>[filteredTypesLength];
115-
116-
for (int i = 0; i < filteredTypesLength; i++)
99+
foreach (var filteredType in filteredTypes)
117100
{
118-
var type = filteredTypes[i];
119-
120-
string fullTypeName = type.FullName;
101+
string fullTypeName = filteredType.FullName;
121102
Assert.IsNotNull(fullTypeName);
122103

123104
if (replaceBuiltInNames)
124105
fullTypeName = fullTypeName.ReplaceWithBuiltInName(true);
125106

126-
typeItems[i] = CreateItem(type, _attribute.Grouping, fullTypeName);
107+
yield return CreateItem(filteredType, _attribute.Grouping, fullTypeName);
127108
}
128-
129-
return typeItems;
130109
}
131110

132-
private void IncludeAdditionalAssemblies(List<Assembly> typeRelatedAssemblies)
111+
private IEnumerable<Assembly> GetTypeRelatedAssemblies(out bool containsMSCorLib)
133112
{
134-
foreach (string assemblyName in _attribute.IncludeAdditionalAssemblies)
135-
{
136-
var additionalAssembly = TypeCollector.TryLoadAssembly(assemblyName);
137-
if (additionalAssembly == null)
138-
continue;
113+
var typeRelatedAssemblies = TypeCollector.GetAssembliesTypeHasAccessTo(_declaringType, out containsMSCorLib);
139114

140-
if ( ! typeRelatedAssemblies.Contains(additionalAssembly))
141-
typeRelatedAssemblies.Add(additionalAssembly);
142-
}
115+
if (_attribute.IncludeAdditionalAssemblies != null)
116+
typeRelatedAssemblies = typeRelatedAssemblies.Concat(GetAdditionalAssemblies());
117+
118+
return typeRelatedAssemblies;
119+
}
120+
121+
private IEnumerable<Assembly> GetAdditionalAssemblies()
122+
{
123+
return _attribute.IncludeAdditionalAssemblies.Select(TypeCollector.TryLoadAssembly).Where(additionalAssembly => additionalAssembly != null);
143124
}
144125
}
145126
}

Editor/ProjectSettings.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,18 @@ public static bool UseBuiltInNames
3434
set => _useBuiltInNames.value = value;
3535
}
3636

37+
private static UserSetting<bool> _showAllTypes;
38+
39+
public static bool ShowAllTypes
40+
{
41+
get
42+
{
43+
InitializeIfNeeded();
44+
return _showAllTypes.value;
45+
}
46+
set => _showAllTypes.value = value;
47+
}
48+
3749
private static void InitializeIfNeeded()
3850
{
3951
if (_instance != null)
@@ -43,6 +55,7 @@ private static void InitializeIfNeeded()
4355

4456
_searchbarMinItemsCount = new UserSetting<int>(_instance, nameof(_searchbarMinItemsCount), 10);
4557
_useBuiltInNames = new UserSetting<bool>(_instance, nameof(_useBuiltInNames), true);
58+
_showAllTypes = new UserSetting<bool>(_instance, nameof(_showAllTypes), false);
4659
}
4760
}
4861
}

Editor/ProjectSettingsDrawer.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ internal static class ProjectSettingsDrawer
1313
private const string BuiltInLabel = "Use built-in names";
1414
private const string BuiltInTooltip = "Whether to make dropdown show built-in types by their keyword name (e.g. int) instead of the full name";
1515

16+
private const string ShowAllTypesLabel = "Show all types";
17+
private const string ShowAllTypesTooltip =
18+
"Whether to show all the types existing in the project in the dropdown. When disabled, only the types the declaring class can reference directly are shown in the dropdown.";
19+
1620
[SettingsProvider]
1721
public static SettingsProvider CreateSettingsProvider()
1822
{
@@ -28,6 +32,7 @@ private static void OnGUI(string searchContext)
2832
using var _ = EditorGUIUtilityHelper.LabelWidthBlock(220f);
2933
ProjectSettings.SearchbarMinItemsCount = EditorGUILayout.IntField(new GUIContent(SearchbarLabel, SearchbarTooltip), ProjectSettings.SearchbarMinItemsCount, GUILayout.MaxWidth(300f));
3034
ProjectSettings.UseBuiltInNames = EditorGUILayout.Toggle(new GUIContent(BuiltInLabel, BuiltInTooltip), ProjectSettings.UseBuiltInNames);
35+
ProjectSettings.ShowAllTypes = EditorGUILayout.Toggle(new GUIContent(ShowAllTypesLabel, ShowAllTypesTooltip), ProjectSettings.ShowAllTypes);
3136
}
3237

3338
private static HashSet<string> GetKeywords()
@@ -37,6 +42,8 @@ private static HashSet<string> GetKeywords()
3742
keywords.AddWords(SearchbarTooltip);
3843
keywords.AddWords(BuiltInLabel);
3944
keywords.AddWords(BuiltInTooltip);
45+
keywords.AddWords(ShowAllTypesLabel);
46+
keywords.AddWords(ShowAllTypesTooltip);
4047
return keywords;
4148
}
4249

Editor/Util/TypeCollector.cs

Lines changed: 31 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
/// </summary>
1414
internal static class TypeCollector
1515
{
16+
private static readonly HashSet<string> _systemAssemblyNames = new HashSet<string> { "mscorlib", "System", "System.Core" };
17+
1618
/// <summary>
1719
/// Collects assemblies the type has access to: the type's native assembly and all its referenced assemblies.
1820
/// </summary>
@@ -25,92 +27,38 @@ internal static class TypeCollector
2527
/// Additional assemblies may be added using <see cref="TypeOptionsAttribute.IncludeAdditionalAssemblies"/>,
2628
/// hence the List return type.
2729
/// </remarks>
28-
public static List<Assembly> GetAssembliesTypeHasAccessTo(Type type)
29-
{
30-
Assembly typeAssembly;
31-
32-
try
33-
{
34-
typeAssembly = type == null ? Assembly.Load("Assembly-CSharp") : type.Assembly;
35-
}
36-
catch (FileNotFoundException)
37-
{
38-
return GetAllAssemblies();
39-
}
40-
41-
var referencedAssemblies = typeAssembly.GetReferencedAssemblies();
42-
var assemblies = new List<Assembly>(referencedAssemblies.Length + 1);
43-
44-
for (int i = 0; i < referencedAssemblies.Length; i++)
45-
{
46-
assemblies[i] = Assembly.Load(referencedAssemblies[i]);
47-
}
48-
49-
assemblies[referencedAssemblies.Length] = typeAssembly;
50-
return assemblies;
51-
}
52-
53-
public static List<Assembly> GetAssembliesTypeHasAccessTo(Type type, out bool containsMscorlib)
30+
public static IEnumerable<Assembly> GetAssembliesTypeHasAccessTo(Type type, out bool containsSystemAssembly)
5431
{
5532
if (type == null)
5633
{
57-
containsMscorlib = true;
34+
containsSystemAssembly = true;
5835
return GetAllAssemblies();
5936
}
6037

61-
containsMscorlib = false;
38+
containsSystemAssembly = false;
6239
Assembly typeAssembly = type.Assembly;
6340

6441
var referencedAssemblies = typeAssembly.GetReferencedAssemblies();
65-
var assemblies = new List<Assembly>(referencedAssemblies.Length + 1);
66-
67-
for (int i = 0; i < referencedAssemblies.Length; i++)
68-
{
69-
var assemblyName = referencedAssemblies[i];
70-
71-
if ( ! containsMscorlib && assemblyName.Name == "mscorlib")
72-
{
73-
containsMscorlib = true;
74-
}
75-
76-
assemblies.Add(Assembly.Load(assemblyName));
77-
}
78-
79-
if ( ! containsMscorlib && typeAssembly.FullName.Contains("mscorlib"))
80-
{
81-
containsMscorlib = true;
82-
}
8342

84-
assemblies.Add(typeAssembly);
85-
86-
return assemblies;
43+
containsSystemAssembly = IsSystemAssembly(typeAssembly) || referencedAssemblies.Any(IsSystemAssembly);
44+
return referencedAssemblies.Select(Assembly.Load).Append(typeAssembly);
8745
}
8846

89-
private static List<Assembly> GetAllAssemblies()
47+
private static bool IsSystemAssembly(Assembly assembly)
9048
{
91-
return new List<Assembly>(AppDomain.CurrentDomain.GetAssemblies());
49+
return IsSystemAssembly(assembly.GetName());
9250
}
93-
94-
public static List<Type> GetFilteredTypesFromAssemblies(
95-
List<Assembly> assemblies,
96-
TypeOptionsAttribute filter)
51+
52+
private static bool IsSystemAssembly(AssemblyName assemblyName)
9753
{
98-
int assembliesCount = assemblies.Count;
99-
100-
var types = new List<Type>(assembliesCount * 20);
101-
102-
for (int i = 0; i < assembliesCount; i++)
103-
{
104-
var filteredTypes = GetFilteredTypesFromAssembly(assemblies[i], filter);
105-
int filteredTypesCount = filteredTypes.Count;
54+
return _systemAssemblyNames.Contains(assemblyName.Name);
55+
}
10656

107-
for (int j = 0; j < filteredTypesCount; j++)
108-
{
109-
types.Add(filteredTypes[j]);
110-
}
111-
}
57+
public static IEnumerable<Assembly> GetAllAssemblies() => AppDomain.CurrentDomain.GetAssemblies();
11258

113-
return types;
59+
public static IEnumerable<Type> GetFilteredTypesFromAssemblies(IEnumerable<Assembly> assemblies, TypeOptionsAttribute filter)
60+
{
61+
return assemblies.SelectMany(assembly => GetFilteredTypesFromAssembly(assembly, filter));
11462
}
11563

11664
public static Assembly TryLoadAssembly(string assemblyName)
@@ -140,66 +88,37 @@ public static Assembly TryLoadAssembly(string assemblyName)
14088
return assembly;
14189
}
14290

143-
private static List<Type> GetFilteredTypesFromAssembly(Assembly assembly, TypeOptionsAttribute filter)
91+
private static IEnumerable<Type> GetFilteredTypesFromAssembly(Assembly assembly, TypeOptionsAttribute filter)
14492
{
145-
List<Type> assemblyTypes;
146-
147-
assemblyTypes = filter.AllowInternal
148-
? GetAllTypesFromAssembly(assembly)
149-
: GetVisibleTypesFromAssembly(assembly);
150-
151-
var filteredTypes = new List<Type>();
152-
int visibleTypesCount = assemblyTypes.Count;
93+
var assemblyTypes = GetAllTypesFromAssembly(assembly);
15394

154-
for (int i = 0; i < visibleTypesCount; i++)
155-
{
156-
Type type = assemblyTypes[i];
157-
if (FilterConstraintIsSatisfied(filter, type))
158-
{
159-
filteredTypes.Add(type);
160-
}
161-
}
95+
// Don't allow internal types of mscorlib even if the AllowInternal property is set to true because it leads
96+
// to a lot of garbage in the dropdown while it's almost impossible users will need a System internal class.
97+
bool allowInternal = filter.AllowInternal && !IsSystemAssembly(assembly);
16298

163-
return filteredTypes;
99+
return assemblyTypes.Where(type => (allowInternal || type.IsVisible) && FilterConstraintIsSatisfied(filter, type));
164100
}
165101

166-
private static List<Type> GetVisibleTypesFromAssembly(Assembly assembly)
102+
private static IEnumerable<Type> GetAllTypesFromAssembly(Assembly assembly)
167103
{
168104
try
169105
{
170-
var assemblyTypes = assembly.GetTypes();
171-
var visibleTypes = new List<Type>(assemblyTypes.Length);
172-
173-
foreach (Type type in assemblyTypes)
174-
{
175-
if (type.IsVisible)
176-
visibleTypes.Add(type);
177-
}
178-
179-
return visibleTypes;
180-
}
181-
catch (ReflectionTypeLoadException e)
182-
{
183-
Debug.LogError($"Types could not be extracted from assembly {assembly}: {e.Message}");
184-
return new List<Type>(0);
185-
}
186-
}
187-
188-
private static List<Type> GetAllTypesFromAssembly(Assembly assembly)
189-
{
190-
try
191-
{
192-
return assembly.GetTypes().ToList();
106+
return assembly.GetTypes();
193107
}
194108
catch (ReflectionTypeLoadException e)
195109
{
196110
Debug.LogError($"Types could not be extracted from assembly {assembly}: {e.Message}");
197-
return new List<Type>(0);
111+
return Enumerable.Empty<Type>();
198112
}
199113
}
200114

201115
private static bool FilterConstraintIsSatisfied(TypeOptionsAttribute filter, Type type)
202116
{
117+
string typeFullName = type.FullName;
118+
119+
if (typeFullName == null || typeFullName.StartsWith("<") || type.Name.StartsWith("<"))
120+
return false;
121+
203122
return filter == null || filter.MatchesRequirements(type);
204123
}
205124
}

0 commit comments

Comments
 (0)