Code/Core/Node.Collection.cs
namespace Nodebox;
public partial class Node {
public class Collection : TypeCollection {
public Collection() : base() { }
public Collection(IEnumerable<SerializableType> nodeTypes) : base(nodeTypes) { }
public Collection(IEnumerable<Type> nodeTypes) : base(nodeTypes.Select(x => new SerializableType(x))) { }
public static Collection From(ICollection<Type> nodeTypes, ICollection<Type> genericArguments) {
var collection = new Collection();
var availableTypes = genericArguments.ToList();
foreach (var nodeType in nodeTypes) {
if (nodeType == typeof(Node)) { continue; }
var nodeTypeDesc = TypeLibrary.GetType(nodeType);
if (!nodeTypeDesc.IsGenericType) {
collection.Add(nodeType);
continue;
}
var genericArgumentsCount = nodeTypeDesc.GenericArguments.Length;
var permuteSource = new List<Type>[genericArgumentsCount];
foreach (var i in Enumerable.Range(0, genericArgumentsCount)) {
permuteSource[i] = availableTypes;
}
foreach (var typeArgs in Permutations.Calculate(permuteSource)) {
if (!TryMakeGenericType(nodeType, typeArgs, out var type)) {
continue;
}
collection.Add(type);
}
}
return collection;
}
public static Collection FromAll(ICollection<Type> genericArguments) =>
From([.. TypeLibrary.GetTypes<Node>().Select(x => x.TargetType)], genericArguments);
public static Collection FromAllWithIntrinsics() =>
FromAll([.. TypeLibrary.IntrinsicTypes.Where(x => !x.IsGenericType)]);
public override IEnumerable<SerializableType> Find(string query, int maxDistance = 2) {
var q = string.IsNullOrEmpty(query) ? "" : query.ToLowerInvariant();
return this
.Select(s => {
var td = TypeLibrary.GetType(s.Type);
var dist = Distance(s.Type, q);
return (item: s, dist, td);
})
.Where(p => !p.td.HasAttribute<HideAttribute>())
.Where(p => p.dist <= maxDistance)
.OrderBy(p => p.dist)
.ThenBy(p => p.td.Name, StringComparer.OrdinalIgnoreCase)
.Select(p => p.item)
.Select(x => {
var attr = TypeLibrary.GetType(x).GetAttribute<IsSpecializationOfAttribute>(false);
if (attr == null) { return x; }
return new SerializableType(attr.Type);
})
.DistinctBy(x => x.GetHashCode());
}
public virtual IEnumerable<SerializableType> FindWithPin(Flow flow, Type pinType, string query, int maxDistance = 2) {
var q = string.IsNullOrEmpty(query) ? "" : query.ToLowerInvariant();
return this
.Select(s => {
var td = TypeLibrary.GetType(s.Type);
var dist = Distance(s.Type, q);
return (item: s, td, dist, name: td.Name);
})
.Where(p => !p.td.HasAttribute<HideAttribute>())
.Where(p => p.dist <= maxDistance)
.Where(p => {
if (pinType == typeof(object)) { return true; }
var (inputPins, outputPins) = GetInitialPins(p.item);
var pins = flow == Flow.Input ? inputPins : outputPins;
return pins.Any(pin => pin.Type == pinType || pin.Type == typeof(object));
})
.OrderBy(p => p.dist)
.ThenBy(p => p.name, StringComparer.OrdinalIgnoreCase)
.Select(p => {
var attr = p.td.GetAttribute<IsSpecializationOfAttribute>(false);
if (attr == null) { return p.item; }
return new SerializableType(attr.Type);
})
.DistinctBy(x => x.GetHashCode());
}
[AttributeUsage(AttributeTargets.Class)]
public class IsSpecializationOfAttribute(Type type) : Attribute {
public Type Type { get; set; } = type;
}
}
}
public class TypeCollection : HashSet<SerializableType> {
public TypeCollection() : base() { }
public TypeCollection(IEnumerable<SerializableType> types) : base(types) { }
public TypeCollection(IEnumerable<Type> types) : base(types.Select(x => new SerializableType(x))) { }
public virtual IEnumerable<SerializableType> Find(string query, int maxDistance = 2) {
var q = string.IsNullOrEmpty(query) ? "" : query.ToLowerInvariant();
return this
.Select(s => (item: s, dist: Distance(s.Type, q), td: TypeLibrary.GetType(s)))
.Where(p => !p.td.HasAttribute<HideAttribute>())
.Where(p => p.dist <= maxDistance)
.OrderBy(p => p.dist)
.ThenBy(p => TypeLibrary.GetType(p.item.Type).Name, StringComparer.OrdinalIgnoreCase)
.Select(p => p.item);
}
protected virtual int Distance(Type type, string queryLower) {
Assert.NotNull(type);
if (string.IsNullOrEmpty(queryLower)) { return 0; }
var typeDescription = TypeLibrary.GetType(type);
int best = 9999999;
bool CheckDistance(string s) {
int len = Math.Min(s.Length, queryLower.Length);
int d = s[..len].ToLowerInvariant().Distance(queryLower);
if (d < best) {
best = d;
if (best == 0) { return true; }
}
return false;
}
if (CheckDistance(typeDescription.Name)) { return best; }
foreach (var tag in typeDescription.Tags) {
if (CheckDistance(tag)) { return best; }
}
foreach (var tag in typeDescription.Aliases) {
if (CheckDistance(tag)) { return best; }
}
return best;
}
}