Skip to content

Instantly share code, notes, and snippets.

@mob-sakai
Last active September 22, 2023 18:33
Show Gist options
  • Save mob-sakai/b98a62c1f2c94c1fef9021101635a5cd to your computer and use it in GitHub Desktop.
Save mob-sakai/b98a62c1f2c94c1fef9021101635a5cd to your computer and use it in GitHub Desktop.
Automatically remove empty folders in project for Unity.
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Linq;
using System;
/// <summary>
/// Remove empty folders automatically.
/// </summary>
public class RemoveEmptyFolders : UnityEditor.AssetModificationProcessor
{
public const string kMenuText = "Assets/Remove Empty Folders";
static readonly StringBuilder s_Log = new StringBuilder();
static readonly List<DirectoryInfo> s_Results = new List<DirectoryInfo>();
/// <summary>
/// Raises the initialize on load method event.
/// </summary>
[InitializeOnLoadMethod]
static void OnInitializeOnLoadMethod()
{
EditorApplication.delayCall += () => Valid();
}
/// <summary>
/// Raises the will save assets event.
/// </summary>
static string[] OnWillSaveAssets(string[] paths)
{
// If menu is unchecked, do nothing.
if (!EditorPrefs.GetBool(kMenuText, false))
return paths;
// Get empty directories in Assets directory
s_Results.Clear();
var assetsDir = Application.dataPath + Path.DirectorySeparatorChar;
GetEmptyDirectories(new DirectoryInfo(assetsDir), s_Results);
// When empty directories has detected, remove the directory.
if (0 < s_Results.Count)
{
s_Log.Length = 0;
s_Log.AppendFormat("Remove {0} empty directories as following:\n", s_Results.Count);
foreach (var d in s_Results)
{
s_Log.AppendFormat("- {0}\n", d.FullName.Replace(assetsDir, ""));
FileUtil.DeleteFileOrDirectory(d.FullName);
}
// UNITY BUG: Debug.Log can not set about more than 15000 characters.
s_Log.Length = Mathf.Min(s_Log.Length, 15000);
Debug.Log(s_Log.ToString());
s_Log.Length = 0;
AssetDatabase.Refresh();
}
return paths;
}
/// <summary>
/// Toggles the menu.
/// </summary>
[MenuItem(kMenuText)]
static void OnClickMenu()
{
// Check/Uncheck menu.
bool isChecked = !Menu.GetChecked(kMenuText);
Menu.SetChecked(kMenuText, isChecked);
// Save to EditorPrefs.
EditorPrefs.SetBool(kMenuText, isChecked);
OnWillSaveAssets(null);
}
[MenuItem(kMenuText, true)]
static bool Valid()
{
// Check/Uncheck menu from EditorPrefs.
Menu.SetChecked(kMenuText, EditorPrefs.GetBool(kMenuText, false));
return true;
}
/// <summary>
/// Get empty directories.
/// </summary>
static bool GetEmptyDirectories(DirectoryInfo dir, List<DirectoryInfo> results)
{
bool isEmpty = true;
try
{
isEmpty = dir.GetDirectories().Count(x => !GetEmptyDirectories(x, results)) == 0 // Are sub directories empty?
&& dir.GetFiles("*.*").All(x => x.Extension == ".meta"); // No file exist?
}
catch
{
}
// Store empty directory to results.
if (isEmpty)
results.Add(dir);
return isEmpty;
}
}
@mob-sakai
Copy link
Author

mob-sakai commented Jul 27, 2017

Overview

Remove empty directories by OnWillSaveAssets callback.
To turn on/off, toggle menu Assets > Remove Empty Folders.
Default is OFF.
image

When empty directories have been removed, put a log.
image

@vijaythakkar
Copy link

Great gist! Would you consider adding an MIT license header to it? :)

@RobergeSimon
Copy link

Great work!

@Lootheo
Copy link

Lootheo commented Sep 13, 2019

Really Good !

@puschie286
Copy link

puschie286 commented Sep 7, 2020

doesnt work with 2020.1 - looks like the deletion of the meta files doesnt work
get following error for all directories that should be deleted
"A meta data file (.meta) exists but its folder '****' can't be found, and has been created. Empty directories cannot be stored in version control, so it's assumed that the meta data file is for an empty directory in version control. When moving or deleting folders outside of Unity, please ensure that the corresponding .meta file is moved or deleted along with it."

@puschie286
Copy link

Found solution for 2020.2+:
delete the meta file instead of the directory

@danoli3
Copy link

danoli3 commented Feb 4, 2021

Solution 2020.2: Lines 48:

FileUtil.DeleteFileOrDirectory(d.FullName);
FileUtil.DeleteFileOrDirectory(d.Parent + "\\" + d.Name + ".meta"); // unity 2020.2 need to delete the meta too

@HernandoNJ
Copy link

Solution 2020.2: Lines 48:

FileUtil.DeleteFileOrDirectory(d.FullName);
FileUtil.DeleteFileOrDirectory(d.Parent + "\\" + d.Name + ".meta"); // unity 2020.2 need to delete the meta too

This worked for me. I wrote it this way

foreach (var d in s_Results)
{
    // Line 1:  s_Log.AppendFormat("- {0}\n", d.FullName.Replace(assetsDir, "")); 
    FileUtil.DeleteFileOrDirectory(d.FullName);
    FileUtil.DeleteFileOrDirectory(d.Parent + "\\" + d.Name + ".meta"); // unity 2020.2 need to delete the meta too
}

I wonder if // Line 1: s_Log.AppendFormat("- {0}\n", d.FullName.Replace(assetsDir, "")); is required

@pointcache
Copy link

pointcache commented Sep 10, 2021

It is achievable without using IO. This is a context version, select folders to clean up, right click > Delete Empty Folders.
You can adapt it for project wide if you dare.

namespace Framework.Editor.Extensions
{
    using UnityEngine;
    using UnityEditor;

    internal static class DeleteEmptyFolders
    {
        [MenuItem("Assets/Delete Empty Folders")]
        static void deleteEmptyFolders()
        {
            foreach (var item in Selection.objects)
            {
                TryDeleteEmptyFolders(item);
            } 
        }

        private static void TryDeleteEmptyFolders(UnityEngine.Object obj)
        {
            if (!AssetDatabase.IsMainAsset(obj))
                return;

            // checks if the asset is a folder
            if (obj.GetType() != typeof(UnityEditor.DefaultAsset))
                return;

            string dir = AssetDatabase.GetAssetPath(obj);

            var subdirs = AssetDatabase.GetSubFolders(dir);

            string[] strBuffer = new string[1];

            foreach (var sub in subdirs)
            {
                strBuffer[0] = sub;
                var assets = AssetDatabase.FindAssets("*", strBuffer);

                if(assets.Length == 0)
                {
                    Debug.Log("Deleteing : " + sub);
                    AssetDatabase.MoveAssetToTrash(sub);
                }
            }
        }
    }
}

@TinyTeaTree
Copy link

Need to change line 97
.All(x => x.Extension == ".meta" || x.Name.Contains("DS_Store")); // No important files?

In Mac there are hidden files called .DS_Store.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment