143 lines
5.8 KiB
C#
143 lines
5.8 KiB
C#
#nullable disable
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using MCPForUnity.Editor.Helpers;
|
|
using UnityEngine;
|
|
|
|
namespace MCPForUnity.Editor.Tools
|
|
{
|
|
/// <summary>
|
|
/// Component resolver that delegates to UnityTypeResolver.
|
|
/// Kept for backwards compatibility.
|
|
/// </summary>
|
|
internal static class ComponentResolver
|
|
{
|
|
/// <summary>
|
|
/// Resolve a Component/MonoBehaviour type by short or fully-qualified name.
|
|
/// Delegates to UnityTypeResolver.TryResolve with Component constraint.
|
|
/// </summary>
|
|
public static bool TryResolve(string nameOrFullName, out Type type, out string error)
|
|
{
|
|
return UnityTypeResolver.TryResolve(nameOrFullName, out type, out error, typeof(Component));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets all accessible property and field names from a component type.
|
|
/// </summary>
|
|
public static List<string> GetAllComponentProperties(Type componentType)
|
|
{
|
|
if (componentType == null) return new List<string>();
|
|
|
|
var properties = componentType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
|
.Where(p => p.CanRead && p.CanWrite)
|
|
.Select(p => p.Name);
|
|
|
|
var fields = componentType.GetFields(BindingFlags.Public | BindingFlags.Instance)
|
|
.Where(f => !f.IsInitOnly && !f.IsLiteral)
|
|
.Select(f => f.Name);
|
|
|
|
// Also include SerializeField private fields (common in Unity)
|
|
var serializeFields = componentType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
|
|
.Where(f => f.GetCustomAttribute<SerializeField>() != null)
|
|
.Select(f => f.Name);
|
|
|
|
return properties.Concat(fields).Concat(serializeFields).Distinct().OrderBy(x => x).ToList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Suggests the most likely property matches for a user's input using fuzzy matching.
|
|
/// Uses Levenshtein distance, substring matching, and common naming pattern heuristics.
|
|
/// </summary>
|
|
public static List<string> GetFuzzyPropertySuggestions(string userInput, List<string> availableProperties)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(userInput) || !availableProperties.Any())
|
|
return new List<string>();
|
|
|
|
var cacheKey = $"{userInput.ToLowerInvariant()}:{string.Join(",", availableProperties)}";
|
|
if (PropertySuggestionCache.TryGetValue(cacheKey, out var cached))
|
|
return cached;
|
|
|
|
try
|
|
{
|
|
var suggestions = GetRuleBasedSuggestions(userInput, availableProperties);
|
|
PropertySuggestionCache[cacheKey] = suggestions;
|
|
return suggestions;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
McpLog.Warn($"[Property Matching] Error getting suggestions for '{userInput}': {ex.Message}");
|
|
return new List<string>();
|
|
}
|
|
}
|
|
|
|
private static readonly Dictionary<string, List<string>> PropertySuggestionCache = new();
|
|
|
|
/// <summary>
|
|
/// Rule-based suggestions that mimic AI behavior for property matching.
|
|
/// This provides immediate value while we could add real AI integration later.
|
|
/// </summary>
|
|
private static List<string> GetRuleBasedSuggestions(string userInput, List<string> availableProperties)
|
|
{
|
|
var suggestions = new List<string>();
|
|
var cleanedInput = userInput.ToLowerInvariant().Replace(" ", "").Replace("-", "").Replace("_", "");
|
|
|
|
foreach (var property in availableProperties)
|
|
{
|
|
var cleanedProperty = property.ToLowerInvariant().Replace(" ", "").Replace("-", "").Replace("_", "");
|
|
|
|
if (cleanedProperty == cleanedInput)
|
|
{
|
|
suggestions.Add(property);
|
|
continue;
|
|
}
|
|
|
|
var inputWords = userInput.ToLowerInvariant().Split(new[] { ' ', '-', '_' }, StringSplitOptions.RemoveEmptyEntries);
|
|
if (inputWords.All(word => cleanedProperty.Contains(word.ToLowerInvariant())))
|
|
{
|
|
suggestions.Add(property);
|
|
continue;
|
|
}
|
|
|
|
if (LevenshteinDistance(cleanedInput, cleanedProperty) <= Math.Max(2, cleanedInput.Length / 4))
|
|
{
|
|
suggestions.Add(property);
|
|
}
|
|
}
|
|
|
|
return suggestions.OrderBy(s => LevenshteinDistance(cleanedInput, s.ToLowerInvariant().Replace(" ", "")))
|
|
.Take(3)
|
|
.ToList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calculates Levenshtein distance between two strings for similarity matching.
|
|
/// </summary>
|
|
private static int LevenshteinDistance(string s1, string s2)
|
|
{
|
|
if (string.IsNullOrEmpty(s1)) return s2?.Length ?? 0;
|
|
if (string.IsNullOrEmpty(s2)) return s1.Length;
|
|
|
|
var matrix = new int[s1.Length + 1, s2.Length + 1];
|
|
|
|
for (int i = 0; i <= s1.Length; i++) matrix[i, 0] = i;
|
|
for (int j = 0; j <= s2.Length; j++) matrix[0, j] = j;
|
|
|
|
for (int i = 1; i <= s1.Length; i++)
|
|
{
|
|
for (int j = 1; j <= s2.Length; j++)
|
|
{
|
|
int cost = (s2[j - 1] == s1[i - 1]) ? 0 : 1;
|
|
matrix[i, j] = Math.Min(Math.Min(
|
|
matrix[i - 1, j] + 1,
|
|
matrix[i, j - 1] + 1),
|
|
matrix[i - 1, j - 1] + cost);
|
|
}
|
|
}
|
|
|
|
return matrix[s1.Length, s2.Length];
|
|
}
|
|
}
|
|
}
|