How to conveniently search components / scripts in hierarchy in Editor UI?

conveniently - fuzzily, with sub-word matching, maybe even using regex patters etc.

right now we have:

  • classical hierarchy search, which is able to only search by full case-sensitive component name,
  • new “Unity Search” package which is an advanced search with many convenient features, but no component search, like the most important thing for me and i suspect for many (?).

if only there was any other package or script which could add a piece of UI with a component search with a useful search algorithm i would be immeasurably thankful

1 Like

I saw this yesterday:

Disclaimer: haven’t touched it yet myself.

thank you. amazing plugin. but it’s not designed for feature I’m looking for. I need a UX to operate on component inspectors in a bulk. much like the classical unity hierarchy search allows you to do, but it requires to enter the component name exactly to find it

Possibly this gonna help you:

using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEditor;
using UnityEditor.SceneManagement;
using System.Collections.Generic;
using System.Linq;

public class ScriptSearchWindow : EditorWindow
{
    private string searchQuery = "";
    private List<GameObject> objectsWithScript = new List<GameObject>();
    private Vector2 scrollPosition;
    private Vector2 scrollPosition2;
    private List<string> scriptNamesLowercase = new List<string>();
    private List<string> scriptNamesOriginal = new List<string>();
    private List<string> searchButton = new List<string>();
    private bool SearchInAssets = false;

    [MenuItem("Window/Script Search Window")]
    public static void ShowWindow()
    {
        var window = GetWindow<ScriptSearchWindow>("Script Search");
    }

    private void OnEnable()
    {
        EditorSceneManager.sceneOpened += OnSceneOpened;
    }

    private void OnDisable()
    {
        EditorSceneManager.sceneOpened -= OnSceneOpened;
    }

    private void OnSceneOpened(Scene scene, OpenSceneMode mode)
    {
        SearchForScript();
    }

    private void OnGUI()
    {
        GUILayout.Label("Search for Script", EditorStyles.boldLabel);
        searchQuery = EditorGUILayout.TextField("Script Name:", searchQuery);

        GUIStyle resetButtonStyle = new GUIStyle(GUI.skin.button);
        resetButtonStyle.normal.textColor = Color.white;
        resetButtonStyle.hover.textColor = Color.white;
        GUI.backgroundColor = new Color(0.8f, 0.1f, 0.1f, 1f);
        if (GUILayout.Button("Reset Input", resetButtonStyle))
        {
            searchQuery = "";
            searchButton.Clear();
        }

        SearchInAssets = EditorGUILayout.Toggle("Search In Assets", SearchInAssets);

        if (searchButton.Contains(searchQuery) || string.IsNullOrEmpty(searchQuery))
        {
            GUI.backgroundColor = new Color(0.1f, 0.1f, 0.8f, 1f);
            if (GUILayout.Button("Search"))
            {
                SearchForScript();
                searchButton.Add(searchQuery);
            }
        }

        GUI.backgroundColor = Color.white;

        GUILayout.Label("Suggestions:");

        float scrollHeight = Mathf.Min(scriptNamesOriginal.Count * EditorGUIUtility.singleLineHeight, position.height * 0.35f);

        scrollPosition = GUILayout.BeginScrollView(scrollPosition, GUILayout.Height(scrollHeight));
       
        if (scriptNamesOriginal.Count > 0)
        {
            HashSet<string> uniqueSuggestions = new HashSet<string>();
            List<string> suggestions = new List<string>();
            foreach (string name in scriptNamesOriginal)
            {
                if (name.ToLower().StartsWith(searchQuery.ToLower()) && !uniqueSuggestions.Contains(name.ToLower()))
                {
                    uniqueSuggestions.Add(name.ToLower());
                    suggestions.Add(name);
                }
            }
           
            if (suggestions.Count > 0)
            {
                foreach (string suggestion in suggestions)
                {
                    EditorGUILayout.BeginHorizontal();
                    GUIStyle suggestionButtonStyle = new GUIStyle(GUI.skin.button);
                    suggestionButtonStyle.normal.textColor = Color.black;
                    suggestionButtonStyle.hover.textColor = Color.black;
                    GUI.backgroundColor = new Color(155f, 155f, 155f, 1f);
                    if (GUILayout.Button(suggestion, suggestionButtonStyle))
                    {
                        searchQuery = suggestion;
                        SearchForScript();
                    }
                    EditorGUILayout.EndHorizontal();
                }
            }
        }
        else
        {
            GUILayout.Label("No Suggestions Available");
        }

        GUI.backgroundColor = Color.white;
       
        GUILayout.EndScrollView();

        GUILayout.Label("Results:");

        scrollPosition2 = GUILayout.BeginScrollView(scrollPosition2);

        if (objectsWithScript.Count > 0)
        {
            foreach (GameObject obj in objectsWithScript)
            {
                if (obj != null)
                {
                    EditorGUILayout.BeginHorizontal();
                    if (GUILayout.Button(GetObjectName(obj), "Button"))
                    {
                        Selection.activeGameObject = obj;
                        if (PrefabUtility.IsPartOfPrefabAsset(obj))
                        {
                            var prefabAsset = PrefabUtility.GetCorrespondingObjectFromSource(obj);
                            if (prefabAsset != null)
                            {
                                AssetDatabase.OpenAsset(prefabAsset);
                            }
                        }
                    }
                    EditorGUILayout.EndHorizontal();
                }
            }
        }
        else
        {
            GUILayout.Label("No Objects With Specified Script Found");
        }

        GUILayout.EndScrollView();
    }

    private void SearchForScript()
    {
        scriptNamesOriginal.Clear();
        scriptNamesLowercase.Clear();
        objectsWithScript.Clear();

        if (!SearchInAssets)
        {
            MonoBehaviour[] scripts = FindObjectsOfType<MonoBehaviour>();
            foreach (MonoBehaviour script in scripts)
            {
                string scriptName = script.GetType().Name;
                scriptNamesOriginal.Add(scriptName);
                scriptNamesLowercase.Add(scriptName.ToLower());
               
                if (scriptName.ToLower().Contains(searchQuery.ToLower()))
                {
                    objectsWithScript.Add(script.gameObject);
                }
            }
        }
       
        if (SearchInAssets)
        {
            MonoBehaviour[] scripts = Resources.FindObjectsOfTypeAll<MonoBehaviour>();
            foreach (MonoBehaviour script in scripts)
            {
                string scriptName = script.GetType().Name;
                scriptNamesOriginal.Add(scriptName);
                scriptNamesLowercase.Add(scriptName.ToLower());
               
                if (scriptName.ToLower().Contains(searchQuery.ToLower()))
                {
                    objectsWithScript.Add(script.gameObject);
                }
            }
        }

        objectsWithScript = objectsWithScript.Distinct().ToList();
    }

    private string GetObjectName(GameObject obj)
    {
        if (PrefabUtility.IsPartOfPrefabAsset(obj))
        {
            return "(Prefab) " + obj.name;
        }
        else
        {
            return obj.name;
        }
    }
}