RealityStop / Bolt.Addons.Community

A community-driven project for extending Unity Bolt
MIT License
250 stars 34 forks source link

Good to have a search node window #68

Closed omega-ult closed 3 months ago

omega-ult commented 3 months ago

I create a search node utility, but I can't just create a pull request since I did a deep customization on my fork, so I will post it here, hope it works for you. Access it from Window -> UVS Community -> Search Node

using System.Collections;
using UnityEditor;
using UnityEngine;
using Unity.VisualScripting;
using System.Text.RegularExpressions;
using System.Linq;
using System;
using System.Collections.Generic;
using System.Reflection;
using Object = UnityEngine.Object;

namespace Unity.VisualScripting.Community
{
    public class SearchNodeWindow : EditorWindow
    {
        // public static Event e;
        enum MatchType
        {
            Type,
            Method,
            Field,
            Reference
        }

        class MatchObject
        {
            public List<MatchType> Matches;
            public ScriptGraphAsset ScriptGraphAsset;
            public StateGraphAsset StateGraphAsset;
            public GraphReference Reference;
            public string FullTypeName;
            public IUnit Unit;
        }

        private string _pattern = "";

        private bool _caseSensitive = true;
        private bool _matchType = true;
        private bool _matchMethod = true;
        private bool _matchField = true;
        private bool _matchReference = true;
        private List<MatchObject> _matchObjects = new();
        private Dictionary<ScriptGraphAsset, List<MatchObject>> _matchScriptGraphMap = new();
        private List<ScriptGraphAsset> _sortedScriptGraphKey = new();
        private Dictionary<StateGraphAsset, List<MatchObject>> _matchStateGraphMap = new();
        private List<StateGraphAsset> _sortedStateGraphKey = new();

        // scroll view position
        private Vector2 _scrollViewRoot;

        [MenuItem("Window/UVS Community/Search Node")]
        public static void Open()
        {
            var window = GetWindow<SearchNodeWindow>();
            window.titleContent = new GUIContent("Search Node");
        }

        private void OnDisable()
        {
            _matchObjects.Clear();
            _matchScriptGraphMap.Clear();
            _sortedScriptGraphKey.Clear();
            _matchStateGraphMap.Clear();
            _sortedStateGraphKey.Clear();
        }

        private void OnGUI()
        {
            // e = Event.current;
            Event e = Event.current;
            if (e.keyCode == KeyCode.Return) Search();

            GUILayout.BeginHorizontal();
            GUILayout.Label("Find", GUILayout.ExpandWidth(false));
            _pattern = GUILayout.TextField(_pattern);
            _caseSensitive = GUILayout.Toggle(_caseSensitive, "MatchCase", GUILayout.ExpandWidth(false));
            if (GUILayout.Button("Search", GUILayout.ExpandWidth(false)))
            {
                // GraphSearch();
                Search();
            }

            GUILayout.EndHorizontal();

            // find arguments.
            GUILayout.BeginHorizontal();
            _matchType = GUILayout.Toggle(_matchType, "Type", GUILayout.ExpandWidth(false));
            _matchMethod = GUILayout.Toggle(_matchMethod, "Method", GUILayout.ExpandWidth(false));
            _matchField = GUILayout.Toggle(_matchField, "Field", GUILayout.ExpandWidth(false));
            _matchReference = GUILayout.Toggle(_matchReference, "Reference", GUILayout.ExpandWidth(false));
            GUILayout.EndHorizontal();

            GUILayout.BeginVertical("box");
            _scrollViewRoot = GUILayout.BeginScrollView(_scrollViewRoot);

            var empty = true;
            // for flow graph asset.
            foreach (var key in _sortedScriptGraphKey)
            {
                var list = _matchScriptGraphMap[key];
                // check show items
                if (!ShouldShowItem(list)) continue;
                EditorGUIUtility.SetIconSize(new Vector2(16, 16));
                var icon = EditorGUIUtility.ObjectContent(key, typeof(ScriptGraphAsset));
                GUILayout.Label(icon);
                // GUILayout.Label(key.name);
                foreach (var match in list)
                {
                    var pathNames = GetUnitPath(match.Reference);
                    var label = $"      {pathNames} : {match.FullTypeName}";
                    if (GUILayout.Button(label, EditorStyles.linkLabel))
                    {
                        FocusMatchObject(match);
                    }
                }

                empty = false;
            }

            // for state graph asset.
            foreach (var key in _sortedStateGraphKey)
            {
                var list = _matchStateGraphMap[key];
                // check show items
                if (!ShouldShowItem(list)) continue;
                EditorGUIUtility.SetIconSize(new Vector2(16, 16));
                var icon = EditorGUIUtility.ObjectContent(key, typeof(ScriptGraphAsset));
                GUILayout.Label(icon);
                // GUILayout.Label(key.name);
                foreach (var match in list)
                {
                    // var parents = match.StateParents.Select(x => x == null ? "" : x.nest.graph.title).ToList();
                    // parents.Add(match.FlowGraph.title);
                    var pathNames = GetUnitPath(match.Reference);
                    var label = $"      {pathNames} : {match.FullTypeName}";
                    if (GUILayout.Button(label, EditorStyles.linkLabel))
                    {
                        FocusMatchObject(match);
                    }
                }

                empty = false;
            }

            if (empty)
            {
                GUILayout.Label("No result found.");
            }

            GUILayout.EndScrollView();
            GUILayout.EndVertical();
        }

        string GetUnitPath(GraphReference reference)
        {
            var nodePath = reference;
            var pathNames = "";
            // ReSharper disable once ConditionIsAlwaysTrueOrFalse
            while (nodePath != null)
            {
                var prefix = "::";
                if (nodePath.graph != null)
                {
                    if (string.IsNullOrEmpty(nodePath.graph.title))
                    {
                        prefix = nodePath.graph.GetType().ToString().Split(".").Last();
                    }
                    else
                    {
                        prefix = nodePath.graph.title;
                    }

                    prefix += "->";
                }

                pathNames = prefix + pathNames; //
                nodePath = nodePath.ParentReference(false);
            }

            return pathNames;
        }

        private void Search()
        {
            _matchObjects.Clear();
            _matchScriptGraphMap.Clear();
            _sortedScriptGraphKey.Clear();
            _matchStateGraphMap.Clear();
            _sortedStateGraphKey.Clear();

            var matchWord = new Regex(_pattern, _caseSensitive ? RegexOptions.None : RegexOptions.IgnoreCase);
            // for script graphs.
            // begin of script graph
            var guids = AssetDatabase.FindAssets("t:ScriptGraphAsset", null);
            foreach (var guid in guids)
            {
                // continue;
                var assetPath = AssetDatabase.GUIDToAssetPath(guid);
                var asset = AssetDatabase.LoadAssetAtPath<ScriptGraphAsset>(assetPath);
                if (asset.GetReference().graph is not FlowGraph flowGraph) continue;
                var baseRef = asset.GetReference().AsReference();
                foreach (var element in TraverseFlowGraph(baseRef))
                {
                    var reference = element.Item1;
                    var unit = element.Item2;
                    var newMatch = MatchUnit(matchWord, unit);
                    if (newMatch == null) continue;
                    newMatch.ScriptGraphAsset = asset;
                    newMatch.Reference = reference;
                    if (_matchScriptGraphMap.TryGetValue(newMatch.ScriptGraphAsset, out var list))
                    {
                        list.Add(newMatch);
                    }
                    else
                    {
                        _matchScriptGraphMap[newMatch.ScriptGraphAsset] = new List<MatchObject>() { newMatch };
                    }
                }
            }

            _sortedScriptGraphKey = _matchScriptGraphMap.Keys.ToList();
            _sortedScriptGraphKey.Sort((a, b) => String.Compare(a.name, b.name, StringComparison.Ordinal));
            // end of script graph

            // begin of script graph
            guids = AssetDatabase.FindAssets("t:StateGraphAsset", null);
            foreach (var guid in guids)
            {
                var assetPath = AssetDatabase.GUIDToAssetPath(guid);
                var asset = AssetDatabase.LoadAssetAtPath<StateGraphAsset>(assetPath);

                var baseRef = asset.GetReference().AsReference();
                foreach (var element in TraverseStateGraph(baseRef))
                {
                    var reference = element.Item1;
                    var unit = element.Item2;
                    var newMatch = MatchUnit(matchWord, unit);
                    if (newMatch == null) continue;
                    newMatch.StateGraphAsset = asset;
                    newMatch.Reference = reference;
                    _matchObjects.Add(newMatch);
                    if (_matchStateGraphMap.TryGetValue(newMatch.StateGraphAsset, out var list))
                    {
                        list.Add(newMatch);
                    }
                    else
                    {
                        _matchStateGraphMap[newMatch.StateGraphAsset] = new List<MatchObject>() { newMatch };
                    }
                }
            }

            _sortedStateGraphKey = _matchStateGraphMap.Keys.ToList();
            _sortedStateGraphKey.Sort((a, b) => String.Compare(a.name, b.name, StringComparison.Ordinal));
        }

        IEnumerable<(List<SuperState>, FlowStateTransition, FlowGraph)> GetSubStates(
            GraphElementCollection<IState> states,
            GraphConnectionCollection<IStateTransition, IState, IState> transitions,
            SuperState parent,
            List<SuperState> nestParent)
        {
            nestParent = new List<SuperState>(nestParent);
            nestParent.Add(parent);

            // var stateGraph = states.nest.graph;
            // yield direct graphs first.
            foreach (var state in states)
            {
                if (state is not FlowState flowState) continue;
                // check flow graphs
                FlowGraph graph = null;
                graph = flowState.nest.embed ?? flowState.nest.graph;

                if (graph == null) continue;
                yield return (nestParent, null, graph);
            }

            // yield transitions.
            foreach (var transition in transitions)
            {
                if (transition is not FlowStateTransition flowStateTransition) continue;
                FlowGraph graph = null;
                graph = flowStateTransition.nest.embed ?? flowStateTransition.nest.graph;

                if (graph == null) continue;
                yield return (nestParent, flowStateTransition, graph);
            }

            // traverse sub states.
            foreach (var subState in states)
            {
                if (subState is not SuperState subSuperState) continue;
                var subStateGraph = subSuperState.nest.graph;
                var subTransitions = subStateGraph.transitions;
                foreach (var item in GetSubStates(subStateGraph.states, subTransitions, subSuperState, nestParent))
                {
                    yield return item;
                }
            }
        }

        IEnumerable<(GraphReference, Unit)> TraverseFlowGraph(GraphReference graphReference)
        {
            var flowGraph = graphReference.graph as FlowGraph;
            if (flowGraph == null) yield break;
            var units = flowGraph.units;
            foreach (var element in units)
            {
                var unit = element as Unit;
                switch (unit)
                {
                    // going deep
                    case SubgraphUnit subgraphUnit:
                    {
                        var subGraph = subgraphUnit.nest.embed ?? subgraphUnit.nest.graph;
                        if (subGraph == null) continue;
                        // find sub graph.
                        var childReference = graphReference.ChildReference(subgraphUnit, false);
                        foreach (var item in TraverseFlowGraph(childReference))
                        {
                            yield return item;
                        }

                        break;
                    }
                    case StateUnit stateUnit:
                    {
                        var stateGraph = stateUnit.nest.embed ?? stateUnit.nest.graph;
                        if (stateGraph == null) continue;
                        // find state graph.
                        var childReference = graphReference.ChildReference(stateUnit, false);
                        foreach (var item in TraverseStateGraph(childReference))
                        {
                            yield return item;
                        }

                        break;
                    }
                    default:
                        yield return (graphReference, unit);
                        break;
                }
            }
        }

        IEnumerable<(GraphReference, Unit)> TraverseStateGraph(GraphReference graphReference)
        {
            var stateGraph = graphReference.graph as StateGraph;
            if (stateGraph == null) yield break;

            // var stateGraph = states.nest.graph;
            // yield direct graphs first.
            foreach (var state in stateGraph.states)
            {
                switch (state)
                {
                    case FlowState flowState:
                    {
                        // check flow graphs, which is the base of a state.
                        var graph = flowState.nest.embed ?? flowState.nest.graph;

                        if (graph == null) continue;
                        var childReference = graphReference.ChildReference(flowState, false);
                        foreach (var item in TraverseFlowGraph(childReference))
                        {
                            yield return item;
                        }

                        break;
                    }
                    case SuperState superState:
                    {
                        // check state graphs
                        var subStateGraph = superState.nest.embed ?? superState.nest.graph;
                        if (subStateGraph == null) continue;
                        var childReference = graphReference.ChildReference(superState, false);
                        foreach (var item in TraverseStateGraph(childReference))
                        {
                            yield return item;
                        }

                        break;
                    }
                    case AnyState:
                        continue;
                }
            }

            // don't forget transition nodes.
            foreach (var transition in stateGraph.transitions)
            {
                if (transition is not FlowStateTransition flowStateTransition) continue;
                var graph = flowStateTransition.nest.embed ?? flowStateTransition.nest.graph;
                if (graph == null) continue;
                var childReference = graphReference.ChildReference(flowStateTransition, false);
                foreach (var item in TraverseFlowGraph(childReference))
                {
                    yield return item;
                }
            }
        }

        MatchObject MatchUnit(Regex matchWord, Unit unit)
        {
            var matchRecord = new MatchObject()
            {
                Matches = new List<MatchType>(),
                ScriptGraphAsset = null,
                StateGraphAsset = null,
                Unit = unit
            };
            // brutal force to create, this can be optimized, but when?
            // match type name.
            var typeName = unit.GetType().ToString().Split(".").Last();
            if (unit is InvokeMember invoker)
            {
                if (invoker.invocation.targetType != null)
                {
                    typeName = invoker.invocation.targetType.ToString().Split(".").Last();
                }

                if (matchWord.IsMatch(typeName))
                {
                    matchRecord.Matches.Add(MatchType.Type);
                }

                try
                {
                    if (matchWord.IsMatch(invoker.invocation.methodInfo.Name))
                    {
                        matchRecord.Matches.Add(MatchType.Method);
                    }
                }
                catch
                {
                    // pass
                }
            }

            matchRecord.FullTypeName = typeName;

            // fit fields
            var fields = unit.GetType()
                .GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);

            var fitField = false;
            // var fitStrings = new List<string>();

            // try read serialized members
            foreach (var kvp in unit.defaultValues)
            {
                var value = kvp.Value;
                if (value == null) continue;
                var serializedValue = value.ToString();
                if (!matchWord.IsMatch(serializedValue)) continue;
                matchRecord.Matches.Add(MatchType.Field);
                fitField = true;
                break;
            }

            // try read class member
            if (!fitField)
            {
                foreach (var field in fields)
                {
                    var value = field.GetValue(unit);
                    if (value == null) continue;
                    var serializedValue = value.ToString();
                    if (!matchWord.IsMatch(serializedValue)) continue;
                    matchRecord.Matches.Add(MatchType.Field);
                    break;
                }
            }

            foreach (var kvp in unit.defaultValues)
            {
                if (kvp.Value is not Object obj) continue;
                if (!AssetDatabase.Contains(obj)) continue;
                var aPath = AssetDatabase.GetAssetPath(obj);
                if (!matchWord.IsMatch(aPath)) continue;

                matchRecord.Matches.Add(MatchType.Reference);
                break;
            }

            return matchRecord.Matches.Count > 0 ? matchRecord : null;
        }

        bool ShouldShowItem(IEnumerable<MatchObject> list)
        {
            foreach (var match in list)
            {
                if (_matchType && match.Matches.Contains(MatchType.Type))
                {
                    return true;
                }

                if (_matchMethod && match.Matches.Contains(MatchType.Method))
                {
                    return true;
                }

                if (_matchField && match.Matches.Contains(MatchType.Field))
                {
                    return true;
                }

                if (_matchReference && match.Matches.Contains(MatchType.Reference))
                {
                    return true;
                }
            }

            return false;
        }

        void FocusMatchObject(MatchObject match)
        {
            var asset = match.ScriptGraphAsset;
            // Locate
            EditorGUIUtility.PingObject(asset);
            Selection.activeObject = asset;

            // open
            GraphReference reference = match.Reference;
            GraphWindow.OpenActive(reference);

            // focus
            var context = reference.Context();
            if (context == null)
                return;
            context.BeginEdit();
            context.canvas?.ViewElements(((IGraphElement)match.Unit).Yield());
        }
    }
}
omega-ult commented 3 months ago

PixPin_2024-05-13_10-46-04

S2NX7 commented 3 months ago

Hey so with this you cannot search for normal nodes with their name? Like for example manual event?

S2NX7 commented 3 months ago

I made a few changes image I wanted to know if I can add this to the package?

omega-ult commented 3 months ago

Sure, I'm happy to see it

S2NX7 commented 3 months ago

Sure, I'm happy to see it

Thanks!

WoolBRKIN commented 3 months ago

Sure, I'm happy to see it

Thanks!

I made a few changes image I wanted to know if I can add this to the package?

Could you share the modified version? Thanks!

S2NX7 commented 3 months ago

Sure, I'm happy to see it

Thanks!

I made a few changes image I wanted to know if I can add this to the package?

Could you share the modified version? Thanks!

Ok just give me a second I am making another change

S2NX7 commented 3 months ago
using UnityEditor;
using UnityEngine;
using Unity.VisualScripting;
using System.Text.RegularExpressions;
using System.Linq;
using System;
using System.Collections.Generic;
using System.Reflection;
using Object = UnityEngine.Object;
using Unity.VisualScripting.Community.Libraries.Humility;
using Unity.VisualScripting.Community.Libraries.CSharp;

namespace Unity.VisualScripting.Community
{
    public class NodeFinderWindow : EditorWindow
    {
        enum MatchType
        {
            Unit,
            Error
        }

        class MatchObject
        {
            public List<MatchType> Matches;
            public ScriptGraphAsset ScriptGraphAsset;
            public ScriptMachine ScriptMachine;
            public StateMachine StateMachine;
            public StateGraphAsset StateGraphAsset;
            public GraphReference Reference;
            public string FullTypeName;
            public IUnit Unit;
        }

        private string _pattern = "";
        private string _previousPattern = ""; // Store the previous text input

        private bool _matchError = true;
        private bool _checkScriptGraphAssets = true;
        private bool _checkStateGraphAssets = true;
        private bool _checkScriptMachines = true;
        private bool _checkStateMachines = true;
        private List<MatchObject> _matchObjects = new();
        private Dictionary<ScriptGraphAsset, List<MatchObject>> _matchScriptGraphMap = new();
        private Dictionary<ScriptMachine, List<MatchObject>> _matchScriptMachineMap = new();
        private Dictionary<StateMachine, List<MatchObject>> _matchStateMachineMap = new();
        private List<ScriptGraphAsset> _sortedScriptGraphKey = new();
        private List<ScriptMachine> _sortedScriptMachineKey = new();
        private List<StateMachine> _sortedStateMachineKey = new();
        private Dictionary<StateGraphAsset, List<MatchObject>> _matchStateGraphMap = new();
        private List<StateGraphAsset> _sortedStateGraphKey = new();
        private float errorCheckInterval = 1.0f;
        private float lastErrorCheckTime;

        // scroll view position
        private Vector2 _scrollViewRoot;

        [MenuItem("Window/Community Addons/Node Finder")]
        public static void Open()
        {
            var window = GetWindow<NodeFinderWindow>();
            window.titleContent = new GUIContent("Node finder");
        }

        private void OnDisable()
        {
            _matchObjects.Clear();
            _matchScriptGraphMap.Clear();
            _sortedScriptGraphKey.Clear();
            _matchScriptMachineMap.Clear();
            _matchStateMachineMap.Clear();
            _sortedScriptMachineKey.Clear();
            _sortedStateMachineKey.Clear();
            _matchStateGraphMap.Clear();
            _sortedStateGraphKey.Clear();
        }

        private void OnEnable()
        {
            _previousPattern = _pattern;
            Search();
        }

        private void OnGUI()
        {
            Event e = Event.current;
            DrawSearchBar();
            GUILayout.Space(6);
            DrawFilters();
            GUILayout.Space(6);
            if (e.keyCode == KeyCode.Return)
            {
                Search();
            }

            if (_pattern != _previousPattern)
            {
                Search();
                _previousPattern = _pattern; // Update the previous pattern
            }

            if (Time.realtimeSinceStartup - lastErrorCheckTime >= errorCheckInterval)
            {
                if (_matchError)
                    SearchForErrors();
                lastErrorCheckTime = Time.realtimeSinceStartup;
            }

            DrawResults();
        }

        private void DrawSearchBar()
        {
            var findLabelStyle = new GUIStyle(LudiqStyles.toolbarLabel)
            {
                fontSize = 12,
                fontStyle = FontStyle.Bold
            };
            HUMEditor.Horizontal().Box(HUMEditorColor.DefaultEditorBackground.Darken(0.1f), Color.black, new RectOffset(0, 0, 0, 0), new RectOffset(1, 1, 1, 1), () =>
                {
                    HUMEditor.Horizontal().Box(HUMEditorColor.DefaultEditorBackground, Color.black, 7, () =>
                                          {
                                              EditorGUILayout.LabelField("Find:", findLabelStyle, GUILayout.Width(40));
                                              _pattern = EditorGUILayout.TextField(_pattern, EditorStyles.toolbarTextField, GUILayout.ExpandWidth(true));
                                          }, false, false);
                });
        }

        private void DrawFilters()
        {
            var filterLabelStyle = new GUIStyle(LudiqStyles.toolbarLabel)
            {
                fontSize = 12,
                alignment = TextAnchor.MiddleCenter,
                fontStyle = FontStyle.Bold
            };

            HUMEditor.Vertical().Box(HUMEditorColor.DefaultEditorBackground.Darken(0.1f), Color.black, new RectOffset(0, 0, 0, 0), new RectOffset(1, 1, 1, 1), () =>
               {
                   GUILayout.Label("Filters:", filterLabelStyle);
                   HUMEditor.Horizontal().Box(HUMEditorColor.DefaultEditorBackground, Color.black, 7, () =>
                                         {
                                             bool prevCheckScriptGraphAssets = _checkScriptGraphAssets;
                                             bool prevCheckStateGraphAssets = _checkStateGraphAssets;
                                             bool prevCheckScriptMachines = _checkScriptMachines;
                                             bool prevCheckStateMachines = _checkStateMachines;
                                             bool prevMatchError = _matchError;

                                             _checkScriptGraphAssets = GUILayout.Toggle(_checkScriptGraphAssets, "ScriptGraphAssets", EditorStyles.toolbarButton);
                                             _checkStateGraphAssets = GUILayout.Toggle(_checkStateGraphAssets, "StateGraphAssets", EditorStyles.toolbarButton);
                                             _checkScriptMachines = GUILayout.Toggle(_checkScriptMachines, "ScriptMachines", EditorStyles.toolbarButton);
                                             _checkStateMachines = GUILayout.Toggle(_checkStateMachines, "StateMachines", EditorStyles.toolbarButton);
                                             _matchError = GUILayout.Toggle(_matchError, "Errors", EditorStyles.toolbarButton);

                                             if (_checkScriptGraphAssets != prevCheckScriptGraphAssets)
                                             {
                                                 Search();
                                             }

                                             if (_checkStateGraphAssets != prevCheckStateGraphAssets)
                                             {
                                                 Search();
                                             }

                                             if (_checkScriptMachines != prevCheckScriptMachines)
                                             {
                                                 Search();
                                             }

                                             if (_checkStateMachines != prevCheckStateMachines)
                                             {
                                                 Search();
                                             }

                                             if (_matchError != prevMatchError)
                                             {
                                                 if (_matchError) SearchForErrors();
                                             }

                                         }, false, false);
               });
        }

        private void DrawResults()
        {
            HUMEditor.Vertical().Box(HUMEditorColor.DefaultEditorBackground.Darken(0.1f), Color.black, new RectOffset(0, 0, 0, 0), new RectOffset(1, 1, 1, 1), () =>
            {
                _scrollViewRoot = EditorGUILayout.BeginScrollView(_scrollViewRoot);

                bool empty = string.IsNullOrEmpty(_pattern) || _matchObjects.Count == 0;
                bool isShowingErrors = false;

                if (!empty)
                {
                    // Display Script Graph results
                    foreach (var key in _sortedScriptGraphKey)
                    {
                        var list = _matchScriptGraphMap[key];
                        if (!ShouldShowItem(list)) continue;

                        EditorGUIUtility.SetIconSize(new Vector2(16, 16));
                        var icon = EditorGUIUtility.ObjectContent(key, typeof(ScriptGraphAsset));
                        var headerStyle = new GUIStyle(LudiqStyles.toolbarLabel)
                        {
                            fontStyle = FontStyle.Bold,
                            fontSize = 14,
                            alignment = TextAnchor.MiddleLeft,
                            richText = true
                        };
                        GUILayout.Label(new GUIContent(key.name, icon.image), headerStyle);

                        foreach (var match in list)
                        {
                            var pathNames = GetUnitPath(match.Reference);
                            if (match.Matches.Contains(MatchType.Error) && _matchError)
                            {
                                isShowingErrors = true;
                                var label = $"      {pathNames} <color=#FF6800>{SearchUtility.HighlightQuery(match.FullTypeName, _pattern)}</color>";

                                // Create the GUIStyle and enable rich text
                                var pathStyle = new GUIStyle(LudiqStyles.paddedButton)
                                {
                                    alignment = TextAnchor.MiddleLeft,
                                    richText = true // Enable rich text
                                };

                                // Display the button with the formatted label
                                if (GUILayout.Button(new GUIContent(label, GetUnitIcon((Unit)match.Unit)), pathStyle))
                                {
                                    FocusMatchObject(match);
                                }
                            }
                            else
                            {
                                var label = $"      {pathNames} {SearchUtility.HighlightQuery(match.FullTypeName, _pattern)}";
                                var pathStyle = new GUIStyle(LudiqStyles.paddedButton)
                                {
                                    alignment = TextAnchor.MiddleLeft,
                                    richText = true
                                };

                                if (GUILayout.Button(new GUIContent(label, GetUnitIcon((Unit)match.Unit)), pathStyle))
                                {
                                    FocusMatchObject(match);
                                }
                            }
                        }
                    }

                    // Display ScriptMachine Graph Results
                    foreach (var key in _sortedScriptMachineKey)
                    {
                        var list = _matchScriptMachineMap[key];
                        if (!ShouldShowItem(list)) continue;
                        EditorGUIUtility.SetIconSize(new Vector2(16, 16));

                        // Using GameObject's default icon
                        var icon = EditorGUIUtility.IconContent("GameObject Icon");
                        var headerStyle = new GUIStyle(LudiqStyles.toolbarLabel)
                        {
                            fontStyle = FontStyle.Bold,
                            fontSize = 14,
                            alignment = TextAnchor.MiddleLeft,
                            richText = true
                        };

                        GUILayout.Label(new GUIContent(key.name + "(ScriptMachine)", icon.image), headerStyle);

                        foreach (var match in list)
                        {
                            var pathNames = GetUnitPath(match.Reference);
                            if (match.Matches.Contains(MatchType.Error) && _matchError)
                            {
                                isShowingErrors = true;
                                var label = $"      {pathNames} <color=#FF6800>{SearchUtility.HighlightQuery(match.FullTypeName, _pattern)}</color>";

                                // Create the GUIStyle and enable rich text
                                var pathStyle = new GUIStyle(LudiqStyles.paddedButton)
                                {
                                    alignment = TextAnchor.MiddleLeft,
                                    richText = true // Enable rich text
                                };

                                // Display the button with the formatted label
                                if (GUILayout.Button(new GUIContent(label, GetUnitIcon((Unit)match.Unit)), pathStyle))
                                {
                                    FocusMatchObject(match);
                                }
                            }
                            else
                            {
                                var label = $"      {pathNames} {SearchUtility.HighlightQuery(match.FullTypeName, _pattern)}";
                                var pathStyle = new GUIStyle(LudiqStyles.paddedButton)
                                {
                                    alignment = TextAnchor.MiddleLeft,
                                    richText = true
                                };

                                if (GUILayout.Button(new GUIContent(label, GetUnitIcon((Unit)match.Unit)), pathStyle))
                                {
                                    FocusMatchObject(match);
                                }
                            }
                        }
                    }

                    // Display StateMachine Graph Results
                    foreach (var key in _sortedStateMachineKey)
                    {
                        var list = _matchStateMachineMap[key];
                        if (!ShouldShowItem(list)) continue;

                        EditorGUIUtility.SetIconSize(new Vector2(16, 16));

                        // Using GameObject's default icon
                        var icon = EditorGUIUtility.IconContent("GameObject Icon");
                        var headerStyle = new GUIStyle(LudiqStyles.toolbarLabel)
                        {
                            fontStyle = FontStyle.Bold,
                            fontSize = 14,
                            alignment = TextAnchor.MiddleLeft,
                            richText = true
                        };

                        GUILayout.Label(new GUIContent(key.name + "(StateMachine)", icon.image), headerStyle);

                        foreach (var match in list)
                        {
                            var pathNames = GetUnitPath(match.Reference);
                            if (match.Matches.Contains(MatchType.Error) && _matchError)
                            {
                                isShowingErrors = true;
                                var label = $"      {pathNames} <color=#FF6800>{SearchUtility.HighlightQuery(match.FullTypeName, _pattern)}</color>";

                                // Create the GUIStyle and enable rich text
                                var pathStyle = new GUIStyle(LudiqStyles.paddedButton)
                                {
                                    alignment = TextAnchor.MiddleLeft,
                                    richText = true // Enable rich text
                                };

                                // Display the button with the formatted label
                                if (GUILayout.Button(new GUIContent(label, GetUnitIcon((Unit)match.Unit)), pathStyle))
                                {
                                    FocusMatchObject(match);
                                }
                            }
                            else
                            {
                                var label = $"      {pathNames} {SearchUtility.HighlightQuery(match.FullTypeName, _pattern)}";
                                var pathStyle = new GUIStyle(LudiqStyles.paddedButton)
                                {
                                    alignment = TextAnchor.MiddleLeft,
                                    richText = true
                                };

                                if (GUILayout.Button(new GUIContent(label, GetUnitIcon((Unit)match.Unit)), pathStyle))
                                {
                                    FocusMatchObject(match);
                                }
                            }
                        }
                    }

                    // Display State Graph results
                    foreach (var key in _sortedStateGraphKey)
                    {
                        var list = _matchStateGraphMap[key];
                        if (!ShouldShowItem(list)) continue;

                        EditorGUIUtility.SetIconSize(new Vector2(16, 16));
                        var icon = EditorGUIUtility.ObjectContent(key, typeof(StateGraphAsset));
                        var headerStyle = new GUIStyle(LudiqStyles.toolbarLabel)
                        {
                            fontStyle = FontStyle.Bold,
                            fontSize = 14,
                            alignment = TextAnchor.MiddleLeft,
                            richText = true
                        };
                        GUILayout.Label(new GUIContent(key.name, icon.image), headerStyle);

                        foreach (var match in list)
                        {
                            var pathNames = GetUnitPath(match.Reference);
                            if (match.Matches.Contains(MatchType.Error) && _matchError)
                            {
                                isShowingErrors = true;
                                var label = $"      {pathNames} <color=#FF6800>{SearchUtility.HighlightQuery(match.FullTypeName, _pattern)}</color>";

                                var pathStyle = new GUIStyle(LudiqStyles.paddedButton)
                                {
                                    alignment = TextAnchor.MiddleLeft,
                                    richText = true
                                };

                                if (GUILayout.Button(new GUIContent(label, GetUnitIcon((Unit)match.Unit)), pathStyle))
                                {
                                    FocusMatchObject(match);
                                }
                            }
                            else
                            {
                                var label = $"      {pathNames} {SearchUtility.HighlightQuery(match.FullTypeName, _pattern)}";
                                var pathStyle = new GUIStyle(LudiqStyles.paddedButton)
                                {
                                    alignment = TextAnchor.MiddleLeft,
                                    richText = true
                                };

                                if (GUILayout.Button(new GUIContent(label, GetUnitIcon((Unit)match.Unit)), pathStyle))
                                {
                                    FocusMatchObject(match);
                                }
                            }
                        }
                    }
                }
                else
                {
                    if (_checkScriptGraphAssets)
                    {
                        foreach (var key in _sortedScriptGraphKey)
                        {
                            var list = _matchScriptGraphMap[key];
                            if (!IsError(list)) continue;
                            isShowingErrors = true;
                            EditorGUIUtility.SetIconSize(new Vector2(16, 16));
                            var icon = EditorGUIUtility.ObjectContent(key, typeof(ScriptGraphAsset));
                            var headerStyle = new GUIStyle(LudiqStyles.toolbarLabel)
                            {
                                fontStyle = FontStyle.Bold,
                                fontSize = 14,
                                alignment = TextAnchor.MiddleLeft,
                                richText = true
                            };
                            GUILayout.Label(new GUIContent(key.name, icon.image), headerStyle);

                            foreach (var match in list)
                            {
                                if (match.Matches.Contains(MatchType.Error))
                                {
                                    var pathNames = GetUnitPath(match.Reference);

                                    var label = $"      {pathNames} <color=#FF6800>{SearchUtility.HighlightQuery(match.FullTypeName, _pattern)}</color>";

                                    // Create the GUIStyle and enable rich text
                                    var pathStyle = new GUIStyle(LudiqStyles.paddedButton)
                                    {
                                        alignment = TextAnchor.MiddleLeft,
                                        richText = true // Enable rich text
                                    };

                                    // Display the button with the formatted label
                                    if (GUILayout.Button(new GUIContent(label, GetUnitIcon((Unit)match.Unit)), pathStyle))
                                    {
                                        FocusMatchObject(match);
                                    }
                                }
                            }
                        }
                    }

                    if (_checkScriptMachines)
                    {
                        // Display ScriptMachine Graph Results
                        foreach (var key in _sortedScriptMachineKey)
                        {
                            var list = _matchScriptMachineMap[key];
                            if (!IsError(list)) continue;
                            isShowingErrors = true;
                            EditorGUIUtility.SetIconSize(new Vector2(16, 16));

                            // Using GameObject's default icon
                            var icon = EditorGUIUtility.IconContent("GameObject Icon");
                            var headerStyle = new GUIStyle(LudiqStyles.toolbarLabel)
                            {
                                fontStyle = FontStyle.Bold,
                                fontSize = 14,
                                alignment = TextAnchor.MiddleLeft,
                                richText = true
                            };
                            GUILayout.Label(new GUIContent(key.name + "(ScriptMachine)", icon.image), headerStyle);

                            foreach (var match in list)
                            {
                                if (match.Matches.Contains(MatchType.Error))
                                {
                                    var pathNames = GetUnitPath(match.Reference);

                                    var label = $"      {pathNames} <color=#FF6800>{SearchUtility.HighlightQuery(match.FullTypeName, _pattern)}</color>";

                                    var pathStyle = new GUIStyle(LudiqStyles.paddedButton)
                                    {
                                        alignment = TextAnchor.MiddleLeft,
                                        richText = true
                                    };

                                    if (GUILayout.Button(new GUIContent(label, GetUnitIcon((Unit)match.Unit)), pathStyle))
                                    {
                                        FocusMatchObject(match);
                                    }
                                }
                            }
                        }
                    }

                    if (_checkStateMachines)
                    {
                        // Display ScriptMachine Graph Results
                        foreach (var key in _sortedStateMachineKey)
                        {
                            var list = _matchStateMachineMap[key];
                            if (!IsError(list)) continue;
                            isShowingErrors = true;
                            EditorGUIUtility.SetIconSize(new Vector2(16, 16));

                            // Using GameObject's default icon
                            var icon = EditorGUIUtility.IconContent("GameObject Icon");
                            var headerStyle = new GUIStyle(LudiqStyles.toolbarLabel)
                            {
                                fontStyle = FontStyle.Bold,
                                fontSize = 14,
                                alignment = TextAnchor.MiddleLeft,
                                richText = true
                            };
                            GUILayout.Label(new GUIContent(key.name + "(StateMachine)", icon.image), headerStyle);

                            foreach (var match in list)
                            {
                                if (match.Matches.Contains(MatchType.Error))
                                {
                                    var pathNames = GetUnitPath(match.Reference);

                                    var label = $"      {pathNames} <color=#FF6800>{SearchUtility.HighlightQuery(match.FullTypeName, _pattern)}</color>";

                                    var pathStyle = new GUIStyle(LudiqStyles.paddedButton)
                                    {
                                        alignment = TextAnchor.MiddleLeft,
                                        richText = true
                                    };

                                    if (GUILayout.Button(new GUIContent(label, GetUnitIcon((Unit)match.Unit)), pathStyle))
                                    {
                                        FocusMatchObject(match);
                                    }
                                }
                            }
                        }
                    }

                    if (_checkStateGraphAssets)
                    {
                        // Display State Graph results
                        foreach (var key in _sortedStateGraphKey)
                        {
                            var list = _matchStateGraphMap[key];
                            if (!IsError(list)) continue;
                            isShowingErrors = true;
                            EditorGUIUtility.SetIconSize(new Vector2(16, 16));
                            var icon = EditorGUIUtility.ObjectContent(key, typeof(StateGraphAsset));
                            var headerStyle = new GUIStyle(LudiqStyles.toolbarLabel)
                            {
                                fontStyle = FontStyle.Bold,
                                fontSize = 14,
                                alignment = TextAnchor.MiddleLeft,
                                richText = true
                            };
                            GUILayout.Label(new GUIContent(key.name, icon.image), headerStyle);

                            foreach (var match in list)
                            {
                                if (match.Matches.Contains(MatchType.Error))
                                {
                                    var pathNames = GetUnitPath(match.Reference);

                                    var label = $"      {pathNames} <color=#FF6800>{SearchUtility.HighlightQuery(match.FullTypeName, _pattern)}</color>";

                                    var pathStyle = new GUIStyle(LudiqStyles.paddedButton)
                                    {
                                        alignment = TextAnchor.MiddleLeft,
                                        richText = true
                                    };

                                    if (GUILayout.Button(new GUIContent(label, GetUnitIcon((Unit)match.Unit)), pathStyle))
                                    {
                                        FocusMatchObject(match);
                                    }
                                }
                            }
                        }
                    }
                }

                if (empty && !isShowingErrors)
                {
                    EditorGUILayout.HelpBox("No results found.", MessageType.Info);
                }

                EditorGUILayout.EndScrollView();
            });
        }

        private Texture GetUnitIcon(Unit unit)
        {
            if (unit is MemberUnit)
            {
                if (unit is InvokeMember invokeMember)
                {
                    var descriptor = invokeMember.Descriptor<InvokeMemberDescriptor>();
                    return descriptor.Icon()[1];
                }
                else if (unit is GetMember getMember)
                {
                    var descriptor = getMember.Descriptor<GetMemberDescriptor>();
                    return descriptor.Icon()[1];
                }
                else
                {
                    var descriptor = unit.Descriptor<SetMemberDescriptor>();
                    return descriptor.Icon()[1];
                }
            }
            else if (unit is Literal literal)
            {
                var descriptor = literal.Descriptor<LiteralDescriptor>();
                return descriptor.Icon()[1];
            }
            else if (unit is UnifiedVariableUnit unifiedVariableUnit)
            {
                var descriptor = unifiedVariableUnit.Descriptor<UnitDescriptor<UnifiedVariableUnit>>();
                return descriptor.Icon()[1];
            }
            else
            {
                var iconDescriptor = unit.GetType().Icon();
                return iconDescriptor[1];
            }
        }

        string GetUnitPath(GraphReference reference)
        {
            var nodePath = reference;
            var pathNames = "";
            // ReSharper disable once ConditionIsAlwaysTrueOrFalse
            while (nodePath != null)
            {
                var prefix = "::";
                if (nodePath.graph != null)
                {
                    if (string.IsNullOrEmpty(nodePath.graph.title))
                    {
                        prefix = nodePath.graph.GetType().ToString().Split(".").Last();
                    }
                    else
                    {
                        prefix = nodePath.graph.title;
                    }

                    prefix += " -> ";
                }

                pathNames = prefix + pathNames;
                nodePath = nodePath.ParentReference(false);
            }

            return pathNames;
        }

        private void SearchForErrors()
        {
            if (_checkScriptGraphAssets)
            {
                var guids = AssetDatabase.FindAssets("t:ScriptGraphAsset", null);
                foreach (var guid in guids)
                {
                    var assetPath = AssetDatabase.GUIDToAssetPath(guid);
                    var asset = AssetDatabase.LoadAssetAtPath<ScriptGraphAsset>(assetPath);
                    if (asset.GetReference().graph is not FlowGraph flowGraph) continue;
                    var baseRef = asset.GetReference().AsReference();
                    foreach (var element in TraverseFlowGraph(baseRef))
                    {
                        var reference = element.Item1;
                        var unit = element.Item2;
                        var newMatch = MatchUnit(unit, baseRef);
                        if (newMatch == null) continue;
                        newMatch.ScriptGraphAsset = asset;
                        newMatch.Reference = reference;
                        _matchObjects.Add(newMatch);
                        if (_matchScriptGraphMap.TryGetValue(newMatch.ScriptGraphAsset, out var list))
                        {
                            if (!list.Any(match => match.Unit == newMatch.Unit))
                            {
                                list.Add(newMatch);
                            }
                            else
                            {
                                list[list.IndexOf(list.First(match => match.Unit == newMatch.Unit))] = newMatch;
                            }
                        }
                        else
                        {
                            _matchScriptGraphMap[newMatch.ScriptGraphAsset] = new List<MatchObject>() { newMatch };
                        }
                    }
                }

                _sortedScriptGraphKey = _matchScriptGraphMap.Keys.ToList();
                _sortedScriptGraphKey.Sort((a, b) => String.Compare(a.name, b.name, StringComparison.Ordinal));
            }

            if (_checkScriptMachines)
            {
                foreach (var machine in UnityObjectUtility.FindObjectsOfTypeIncludingInactive<ScriptMachine>().Where(_asset => _asset.nest.source == GraphSource.Embed))
                {
                    if (machine == null || machine.GetReference() == null || machine.GetReference().graph is not FlowGraph flowGraph) continue;
                    var baseRef = machine.GetReference().AsReference();
                    foreach (var element in TraverseFlowGraph(baseRef))
                    {
                        var reference = element.Item1;
                        var unit = element.Item2;
                        var newMatch = MatchUnit(unit, baseRef);
                        if (newMatch == null) continue;
                        newMatch.ScriptMachine = machine;
                        newMatch.Reference = reference;
                        _matchObjects.Add(newMatch);
                        if (_matchScriptMachineMap.TryGetValue(newMatch.ScriptMachine, out var list))
                        {
                            if (!list.Any(match => match.Unit == newMatch.Unit))
                            {
                                list.Add(newMatch);
                            }
                            else
                            {
                                list[list.IndexOf(list.First(match => match.Unit == newMatch.Unit))] = newMatch;
                            }
                        }
                        else
                        {
                            _matchScriptMachineMap[newMatch.ScriptMachine] = new List<MatchObject>() { newMatch };
                        }
                    }
                }

                _sortedScriptMachineKey = _matchScriptMachineMap.Keys.ToList();
                _sortedScriptMachineKey.Sort((a, b) => string.Compare(a.nest.graph.title, b.nest.graph.title, StringComparison.Ordinal));
            }

            if (_checkStateMachines)
            {
                foreach (var machine in UnityObjectUtility.FindObjectsOfTypeIncludingInactive<StateMachine>().Where(_asset => _asset.nest.source == GraphSource.Embed))
                {
                    if (machine == null || machine.GetReference() == null || machine.GetReference().graph is not StateGraph flowGraph) continue;
                    var baseRef = machine.GetReference().AsReference();
                    foreach (var element in TraverseStateGraph(baseRef))
                    {
                        var reference = element.Item1;
                        var unit = element.Item2;
                        var newMatch = MatchUnit(unit, baseRef);
                        if (newMatch == null) continue;
                        newMatch.StateMachine = machine;
                        newMatch.Reference = reference;
                        _matchObjects.Add(newMatch);
                        if (_matchStateMachineMap.TryGetValue(newMatch.StateMachine, out var list))
                        {
                            if (!list.Any(match => match.Unit == newMatch.Unit))
                            {
                                list.Add(newMatch);
                            }
                            else
                            {
                                list[list.IndexOf(list.First(match => match.Unit == newMatch.Unit))] = newMatch;
                            }
                        }
                        else
                        {
                            _matchStateMachineMap[newMatch.StateMachine] = new List<MatchObject>() { newMatch };
                        }
                    }
                }

                _sortedStateMachineKey = _matchStateMachineMap.Keys.ToList();
                _sortedStateMachineKey.Sort((a, b) => string.Compare(a.nest.graph.title, b.nest.graph.title, StringComparison.Ordinal));
            }

            if (_checkStateGraphAssets)
            {
                var guids = AssetDatabase.FindAssets("t:StateGraphAsset", null);
                foreach (var guid in guids)
                {
                    var assetPath = AssetDatabase.GUIDToAssetPath(guid);
                    var asset = AssetDatabase.LoadAssetAtPath<StateGraphAsset>(assetPath);

                    var baseRef = asset.GetReference().AsReference();
                    foreach (var element in TraverseStateGraph(baseRef))
                    {
                        var reference = element.Item1;
                        var unit = element.Item2;
                        var newMatch = MatchUnit(unit, baseRef);
                        if (newMatch == null) continue;
                        newMatch.StateGraphAsset = asset;
                        newMatch.Reference = reference;
                        _matchObjects.Add(newMatch);
                        if (_matchScriptGraphMap.TryGetValue(newMatch.ScriptGraphAsset, out var list))
                        {
                            if (!list.Any(match => match.Unit == newMatch.Unit))
                            {
                                list.Add(newMatch);
                            }
                            else
                            {
                                list[list.IndexOf(list.First(match => match.Unit == newMatch.Unit))] = newMatch;
                            }
                        }
                        else
                        {
                            _matchScriptGraphMap[newMatch.ScriptGraphAsset] = new List<MatchObject>() { newMatch };
                        }
                    }
                }

                _sortedStateGraphKey = _matchStateGraphMap.Keys.ToList();
                _sortedStateGraphKey.Sort((a, b) => String.Compare(a.name, b.name, StringComparison.Ordinal));
            }
        }

        private void Search()
        {
            _matchObjects.Clear();
            _matchScriptGraphMap.Clear();
            _sortedScriptGraphKey.Clear();
            _matchStateGraphMap.Clear();
            _sortedStateGraphKey.Clear();
            _matchScriptMachineMap.Clear();
            _sortedScriptMachineKey.Clear();
            _matchStateMachineMap.Clear();
            _sortedStateMachineKey.Clear();

            var matchWord = new Regex(_pattern, RegexOptions.IgnoreCase);
            // for script graphs.
            // begin of script graph

            if (_checkScriptGraphAssets)
            {
                var guids = AssetDatabase.FindAssets("t:ScriptGraphAsset", null);
                foreach (var guid in guids)
                {
                    // continue;
                    var assetPath = AssetDatabase.GUIDToAssetPath(guid);
                    var asset = AssetDatabase.LoadAssetAtPath<ScriptGraphAsset>(assetPath);
                    if (asset.GetReference().graph is not FlowGraph flowGraph) continue;
                    var baseRef = asset.GetReference().AsReference();
                    foreach (var element in TraverseFlowGraph(baseRef))
                    {
                        var reference = element.Item1;
                        var unit = element.Item2;
                        var newMatch = MatchUnit(matchWord, unit);
                        if (newMatch == null) continue;
                        newMatch.ScriptGraphAsset = asset;
                        newMatch.Reference = reference;
                        _matchObjects.Add(newMatch);
                        if (_matchScriptGraphMap.TryGetValue(newMatch.ScriptGraphAsset, out var list))
                        {
                            list.Add(newMatch);
                        }
                        else
                        {
                            _matchScriptGraphMap[newMatch.ScriptGraphAsset] = new List<MatchObject>() { newMatch };
                        }
                    }
                }

                _sortedScriptGraphKey = _matchScriptGraphMap.Keys.ToList();
                _sortedScriptGraphKey.Sort((a, b) => String.Compare(a.name, b.name, StringComparison.Ordinal));
            }

            if (_checkScriptMachines)
            {
                foreach (var machine in UnityObjectUtility.FindObjectsOfTypeIncludingInactive<ScriptMachine>().Where(_asset => _asset.nest.source == GraphSource.Embed))
                {
                    if (machine == null || machine.GetReference() == null || machine.GetReference().graph is not FlowGraph flowGraph) continue;
                    var baseRef = machine.GetReference().AsReference();
                    foreach (var element in TraverseFlowGraph(baseRef))
                    {
                        var reference = element.Item1;
                        var unit = element.Item2;
                        var newMatch = MatchUnit(matchWord, unit);
                        if (newMatch == null) continue;
                        newMatch.ScriptMachine = machine;
                        newMatch.Reference = reference;
                        _matchObjects.Add(newMatch);
                        if (_matchScriptMachineMap.TryGetValue(newMatch.ScriptMachine, out var list))
                        {
                            list.Add(newMatch);
                        }
                        else
                        {
                            _matchScriptMachineMap[newMatch.ScriptMachine] = new List<MatchObject>() { newMatch };
                        }
                    }
                }

                _sortedScriptMachineKey = _matchScriptMachineMap.Keys.ToList();
                _sortedScriptMachineKey.Sort((a, b) => string.Compare(a.nest.graph.title, b.nest.graph.title, StringComparison.Ordinal));
            }

            if (_checkStateMachines)
            {
                foreach (var machine in UnityObjectUtility.FindObjectsOfTypeIncludingInactive<StateMachine>().Where(_asset => _asset.nest.source == GraphSource.Embed))
                {
                    if (machine == null || machine.GetReference() == null || machine.GetReference().graph is not StateGraph stateGraph) continue;
                    var baseRef = machine.GetReference().AsReference();
                    foreach (var element in TraverseStateGraph(baseRef))
                    {
                        var reference = element.Item1;
                        var unit = element.Item2;
                        var newMatch = MatchUnit(matchWord, unit);
                        if (newMatch == null) continue;
                        newMatch.StateMachine = machine;
                        newMatch.Reference = reference;
                        _matchObjects.Add(newMatch);
                        if (_matchStateMachineMap.TryGetValue(newMatch.StateMachine, out var list))
                        {
                            list.Add(newMatch);
                        }
                        else
                        {
                            _matchStateMachineMap[newMatch.StateMachine] = new List<MatchObject>() { newMatch };
                        }
                    }
                }

                _sortedStateMachineKey = _matchStateMachineMap.Keys.ToList();
                _sortedStateMachineKey.Sort((a, b) => string.Compare(a.nest.graph.title, b.nest.graph.title, StringComparison.Ordinal));
            }

            if (_checkStateGraphAssets)
            {
                var guids = AssetDatabase.FindAssets("t:StateGraphAsset", null);
                foreach (var guid in guids)
                {
                    var assetPath = AssetDatabase.GUIDToAssetPath(guid);
                    var asset = AssetDatabase.LoadAssetAtPath<StateGraphAsset>(assetPath);

                    var baseRef = asset.GetReference().AsReference();
                    foreach (var element in TraverseStateGraph(baseRef))
                    {
                        var reference = element.Item1;
                        var unit = element.Item2;
                        var newMatch = MatchUnit(matchWord, unit);
                        if (newMatch == null) continue;
                        newMatch.StateGraphAsset = asset;
                        newMatch.Reference = reference;
                        _matchObjects.Add(newMatch);
                        if (_matchStateGraphMap.TryGetValue(newMatch.StateGraphAsset, out var list))
                        {
                            if (!list.Any(match => match.Unit == newMatch.Unit))
                            {
                                list.Add(newMatch);
                            }
                        }
                        else
                        {
                            _matchStateGraphMap[newMatch.StateGraphAsset] = new List<MatchObject>() { newMatch };
                        }
                    }
                }

                _sortedStateGraphKey = _matchStateGraphMap.Keys.ToList();
                _sortedStateGraphKey.Sort((a, b) => String.Compare(a.name, b.name, StringComparison.Ordinal));
            }
        }

        IEnumerable<(List<SuperState>, FlowStateTransition, FlowGraph)> GetSubStates(
            GraphElementCollection<IState> states,
            GraphConnectionCollection<IStateTransition, IState, IState> transitions,
            SuperState parent,
            List<SuperState> nestParent)
        {
            nestParent = new List<SuperState>(nestParent)
            {
                parent
            };
            // var stateGraph = states.nest.graph;
            // yield direct graphs first.
            foreach (var state in states)
            {
                if (state is not FlowState flowState) continue;
                // check flow graphs
                FlowGraph graph = null;
                graph = flowState.nest.embed ?? flowState.nest.graph;

                if (graph == null) continue;
                yield return (nestParent, null, graph);
            }

            // yield transitions.
            foreach (var transition in transitions)
            {
                if (transition is not FlowStateTransition flowStateTransition) continue;
                FlowGraph graph = null;
                graph = flowStateTransition.nest.embed ?? flowStateTransition.nest.graph;

                if (graph == null) continue;
                yield return (nestParent, flowStateTransition, graph);
            }

            // traverse sub states.
            foreach (var subState in states)
            {
                if (subState is not SuperState subSuperState) continue;
                var subStateGraph = subSuperState.nest.graph;
                var subTransitions = subStateGraph.transitions;
                foreach (var item in GetSubStates(subStateGraph.states, subTransitions, subSuperState, nestParent))
                {
                    yield return item;
                }
            }
        }

        IEnumerable<(GraphReference, Unit)> TraverseFlowGraph(GraphReference graphReference)
        {
            var flowGraph = graphReference.graph as FlowGraph;
            if (flowGraph == null) yield break;
            var units = flowGraph.units;
            foreach (var element in units)
            {
                var unit = element as Unit;
                switch (unit)
                {
                    // going deep
                    case SubgraphUnit subgraphUnit:
                        {
                            var subGraph = subgraphUnit.nest.embed ?? subgraphUnit.nest.graph;
                            if (subGraph == null) continue;
                            yield return (graphReference, subgraphUnit);
                            // find sub graph.
                            var childReference = graphReference.ChildReference(subgraphUnit, false);
                            foreach (var item in TraverseFlowGraph(childReference))
                            {
                                yield return item;
                            }

                            break;
                        }
                    case StateUnit stateUnit:
                        {
                            var stateGraph = stateUnit.nest.embed ?? stateUnit.nest.graph;
                            if (stateGraph == null) continue;
                            // find state graph.
                            var childReference = graphReference.ChildReference(stateUnit, false);
                            foreach (var item in TraverseStateGraph(childReference))
                            {
                                yield return item;
                            }

                            break;
                        }
                    default:
                        yield return (graphReference, unit);
                        break;
                }
            }
        }
        private bool HandleSearch(IUnit unit, out string name)
        {
            if (unit is MemberUnit memberUnit)
            {
                var _name = memberUnit.member.ToPseudoDeclarer().ToString();
                name = _name;
                return SearchUtility.Matches(SearchUtility.Relevance(_pattern, _name));
            }
            else if (unit is SubgraphUnit subgraphUnit)
            {
                var _name = subgraphUnit.nest != null ? GetGraphName(subgraphUnit.nest.graph).Replace("Graph", "Subgraph") : "Subgraph";
                name = _name;
                return SearchUtility.Matches(SearchUtility.Relevance(_pattern, _name));
            }
            else
            {
                var _name = BoltFlowNameUtility.UnitTitle(unit.GetType(), false, false);
                name = _name;
                return SearchUtility.Matches(SearchUtility.Relevance(_pattern, _name));
            }
        }

        IEnumerable<(GraphReference, Unit)> TraverseStateGraph(GraphReference graphReference)
        {
            var stateGraph = graphReference.graph as StateGraph;
            if (stateGraph == null) yield break;

            // var stateGraph = states.nest.graph;
            // yield direct graphs first.
            foreach (var state in stateGraph.states)
            {
                switch (state)
                {
                    case FlowState flowState:
                        {
                            // check flow graphs, which is the base of a state.
                            var graph = flowState.nest.embed ?? flowState.nest.graph;

                            if (graph == null) continue;
                            var childReference = graphReference.ChildReference(flowState, false);
                            foreach (var item in TraverseFlowGraph(childReference))
                            {
                                yield return item;
                            }

                            break;
                        }
                    case SuperState superState:
                        {
                            // check state graphs
                            var subStateGraph = superState.nest.embed ?? superState.nest.graph;
                            if (subStateGraph == null) continue;
                            var childReference = graphReference.ChildReference(superState, false);
                            foreach (var item in TraverseStateGraph(childReference))
                            {
                                yield return item;
                            }

                            break;
                        }
                    case AnyState:
                        continue;
                }
            }

            // don't forget transition nodes.
            foreach (var transition in stateGraph.transitions)
            {
                if (transition is not FlowStateTransition flowStateTransition) continue;
                var graph = flowStateTransition.nest.embed ?? flowStateTransition.nest.graph;
                if (graph == null) continue;
                var childReference = graphReference.ChildReference(flowStateTransition, false);
                foreach (var item in TraverseFlowGraph(childReference))
                {
                    yield return item;
                }
            }
        }

        private MatchObject MatchUnit(Regex matchWord, Unit unit)
        {
            var matchRecord = new MatchObject
            {
                Matches = new List<MatchType>(),
                Unit = unit,
                FullTypeName = GetUnitFullName(unit)
            };

            CheckMemberUnit(matchWord, unit, matchRecord);
            CheckLiteralUnit(unit, matchRecord);
            CheckFields(matchWord, unit, matchRecord);
            CheckDefaultValues(matchWord, unit, matchRecord);
            CheckAssetReferences(matchWord, unit, matchRecord);

            if (HandleSearch(unit, out string name))
            {
                matchRecord.FullTypeName = GetFullNameWithInputs(unit, name);
                matchRecord.Matches.Add(MatchType.Unit);
            }

            return matchRecord.Matches.Count > 0 ? matchRecord : null;
        }

        private MatchObject MatchUnit(Unit unit, GraphReference baseRef)
        {
            var matchRecord = new MatchObject
            {
                Matches = new List<MatchType>(),
                Unit = unit,
                FullTypeName = GetFullNameWithInputs(unit, GetUnitFullName(unit))
            };

            if (unit.GetException(baseRef) != null || unit is MissingType)
            {
                if (unit.GetException(baseRef) != null)
                {
                    matchRecord.FullTypeName += $" ({unit.GetException(baseRef).Message})";
                }
                else if (unit is MissingType missingType)
                {
                    matchRecord.FullTypeName += $" {(missingType.formerType == null ? "Missing Type" : "Missing Type : " + missingType.formerType)}";
                }
                matchRecord.Matches.Add(MatchType.Error);
            }

            return matchRecord.Matches.Count > 0 ? matchRecord : null;
        }

        private string GetUnitFullName(Unit unit)
        {
            var typeName = GetUnitName(unit);

            if (unit is MemberUnit invoker && invoker.member.targetType != null)
            {
                typeName = invoker.member.ToPseudoDeclarer().ToString();
            }

            return typeName;
        }

        private void CheckMemberUnit(Regex matchWord, Unit unit, MatchObject matchRecord)
        {
            if (unit is MemberUnit)
            {
                if (matchWord.IsMatch(matchRecord.FullTypeName))
                {
                    matchRecord.Matches.Add(MatchType.Unit);
                }
            }
        }

        private void CheckLiteralUnit(Unit unit, MatchObject matchRecord)
        {
            if (unit is Literal literal)
            {
                matchRecord.FullTypeName = $"{matchRecord.FullTypeName} (Type : {literal.type.As().CSharpName(false, false, false)}, Value : {literal.value})";
            }
            else if (unit.valueInputs.Count > 0)
            {
                matchRecord.FullTypeName += $" : ({string.Join(", ", unit.valueInputs.Select(port => GetValue(port)))})";
            }
        }

        private void CheckFields(Regex matchWord, Unit unit, MatchObject matchRecord)
        {
            var fields = unit.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);

            foreach (var field in fields)
            {
                var value = field.GetValue(unit);
                if (value == null) continue;
                if (matchWord.IsMatch(value.ToString()))
                {
                    matchRecord.Matches.Add(MatchType.Unit);
                    break;
                }
            }
        }

        private void CheckDefaultValues(Regex matchWord, Unit unit, MatchObject matchRecord)
        {
            foreach (var kvp in unit.defaultValues)
            {
                var value = kvp.Value;
                if (value == null) continue;
                if (matchWord.IsMatch(value.ToString()))
                {
                    matchRecord.Matches.Add(MatchType.Unit);
                    matchRecord.FullTypeName += $" ({kvp.Key.LegalMemberName().Prettify()} : {(value is Type type ? type.HumanName() : value)})";
                    break;
                }
            }
        }

        private void CheckAssetReferences(Regex matchWord, Unit unit, MatchObject matchRecord)
        {
            foreach (var kvp in unit.defaultValues)
            {
                if (kvp.Value is not Object obj) continue;
                if (!AssetDatabase.Contains(obj)) continue;
                if (matchWord.IsMatch(AssetDatabase.GetAssetPath(obj)))
                {
                    matchRecord.Matches.Add(MatchType.Unit);
                    matchRecord.FullTypeName += $" ({kvp.Key.LegalMemberName().Prettify()} : {kvp.Value})";
                    break;
                }
            }
        }

        private string GetFullNameWithInputs(Unit unit, string baseName)
        {
            if (unit is Literal literal)
            {
                return $"{baseName} (Type : {literal.type.As().CSharpName(false, false, false)}, Value : {literal.value})";
            }
            else if (unit is MemberUnit memberUnit && memberUnit.member.targetType != null)
            {
                return $"{memberUnit.member.ToPseudoDeclarer()} : ({string.Join(", ", unit.valueInputs.Select(port => GetValue(port)))})";
            }
            else if (unit.valueInputs.Count > 0)
            {
                return $"{baseName} : ({string.Join(", ", unit.valueInputs.Select(port => GetValue(port)))})";
            }
            return baseName;
        }

        private string GetUnitName(Unit unit)
        {
            return BoltFlowNameUtility.UnitTitle(unit.GetType(), false, false);
        }

        private string GetValue(ValueInput valueInput)
        {
            if (valueInput.hasDefaultValue)
            {

                return $"{valueInput.key.LegalMemberName().Prettify()} : " + (!valueInput.nullMeansSelf ? valueInput.unit.defaultValues[valueInput.key] is Type type ? type.HumanName() : valueInput.unit.defaultValues[valueInput.key]?.ToString() ?? "null" : "This");
            }
            else if (valueInput.hasAnyConnection)
            {
                if (valueInput.hasValidConnection)
                {
                    return $"{valueInput.key.LegalMemberName().Prettify()} : Connected To : " + GetUnitName(valueInput.connection.source.unit as Unit);
                }
                else if (valueInput.hasInvalidConnection)
                {
                    return $"{valueInput.key.LegalMemberName().Prettify()} : Invalid Connection";
                }
            }

            return $"{valueInput.key.LegalMemberName().Prettify()} : No Value";
        }

        bool ShouldShowItem(IEnumerable<MatchObject> list)
        {
            foreach (var match in list)
            {
                if (match.Matches.Contains(MatchType.Unit))
                {
                    return true;
                }
            }

            return false;
        }

        bool IsError(IEnumerable<MatchObject> list)
        {
            foreach (var match in list)
            {
                if (_matchError && match.Matches.Contains(MatchType.Error))
                {
                    return true;
                }
            }

            return false;
        }

        private string GetGraphName(Graph graph)
        {
            return !string.IsNullOrEmpty(graph.title) ? graph.title : "Unnamed Graph";
        }

        void FocusMatchObject(MatchObject match)
        {
            if (match.ScriptGraphAsset != null)
            {
                var asset = match.ScriptGraphAsset;
                // Locate
                EditorGUIUtility.PingObject(asset);
                Selection.activeObject = asset;
            }
            else if (match.StateGraphAsset != null)
            {
                var asset = match.StateGraphAsset;
                // Locate
                EditorGUIUtility.PingObject(asset);
                Selection.activeObject = asset;
            }
            else if (match.ScriptMachine != null)
            {
                var machine = match.ScriptMachine.gameObject;
                // Locate
                EditorGUIUtility.PingObject(machine);
                Selection.activeObject = machine;
            }

            // open
            GraphReference reference = match.Reference;
            GraphWindow.OpenActive(reference);

            // focus
            var context = reference.Context();
            if (context == null)
                return;
            context.BeginEdit();
            context.canvas?.ViewElements(((IGraphElement)match.Unit).Yield());
        }
    }
}