using System; using System.Collections.Generic; using System.Linq; using Newtonsoft.Json.Linq; using UnityEditor; using MCPForUnity.Editor.Helpers; namespace MCPForUnity.Editor.Tools.MenuItems { /// /// Provides read/list/exists capabilities for Unity menu items with caching. /// public static class MenuItemsReader { private static List _cached; [InitializeOnLoadMethod] private static void Build() => Refresh(); /// /// Returns the cached list, refreshing if necessary. /// public static IReadOnlyList AllMenuItems() => _cached ??= Refresh(); /// /// Rebuilds the cached list from reflection. /// private static List Refresh() { try { var methods = TypeCache.GetMethodsWithAttribute(); _cached = methods // Methods can have multiple [MenuItem] attributes; collect them all .SelectMany(m => m .GetCustomAttributes(typeof(MenuItem), false) .OfType() .Select(attr => attr.menuItem)) .Where(s => !string.IsNullOrEmpty(s)) .Distinct(StringComparer.Ordinal) // Ensure no duplicates .OrderBy(s => s, StringComparer.Ordinal) // Ensure consistent ordering .ToList(); return _cached; } catch (Exception e) { McpLog.Error($"[MenuItemsReader] Failed to scan menu items: {e}"); _cached = _cached ?? new List(); return _cached; } } /// /// Returns a list of menu items. Optional 'search' param filters results. /// public static object List(JObject @params) { string search = @params["search"]?.ToString(); bool doRefresh = @params["refresh"]?.ToObject() ?? false; if (doRefresh || _cached == null) { Refresh(); } IEnumerable result = _cached ?? Enumerable.Empty(); if (!string.IsNullOrEmpty(search)) { result = result.Where(s => s.IndexOf(search, StringComparison.OrdinalIgnoreCase) >= 0); } return Response.Success("Menu items retrieved.", result.ToList()); } /// /// Checks if a given menu path exists in the cache. /// public static object Exists(JObject @params) { string menuPath = @params["menu_path"]?.ToString() ?? @params["menuPath"]?.ToString(); if (string.IsNullOrWhiteSpace(menuPath)) { return Response.Error("Required parameter 'menu_path' or 'menuPath' is missing or empty."); } bool doRefresh = @params["refresh"]?.ToObject() ?? false; if (doRefresh || _cached == null) { Refresh(); } bool exists = (_cached ?? new List()).Contains(menuPath); return Response.Success($"Exists check completed for '{menuPath}'.", new { exists }); } } }