What I want to do is the following: determine the path of the class file in which to find a specified C# class (non-monobehaviour/ScriptableObject). I already know how to find monobehaviour scripts.
I’ve looked on the C# boards but it is mostly concerned with how to find a class library (.dll) which contains a certain class. It’s probably not a situation that happens outside of this environment.
Here in 2023. I offer my code as another solution to this problem. You will require a few code analysis Dlls, but so far it works great and allowed me to open C# files directly from a right-click menu. I have something like 80-100 .cs files in my project and no SSD on the machine I’m using. Generally runs in about 1/4 of a second, but you could probably optimize if you want. I’m not sure what it does with generics, I haven’t tested that.
/// <summary>
/// Utility class for code analysis.
/// </summary>
public static class CodeAnalysis
{
private static Dictionary<string, string> ClassNameToFileMapping = new Dictionary<string, string>();
/// <summary>
/// When this class is first loaded, we'll compile a list of all classes.
/// </summary>
static CodeAnalysis()
{
string[] cSharpFiles = Directory.GetFiles(Application.dataPath, "*.cs", SearchOption.AllDirectories);
foreach (string cSharpFile in cSharpFiles)
{
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(File.ReadAllText(cSharpFile));
var root = syntaxTree.GetCompilationUnitRoot();
foreach (SyntaxNode node in root.DescendantNodes())
{
if (node is TypeDeclarationSyntax syntaxNode)
{
string fullname = syntaxNode.Identifier.ValueText;
IEnumerable<SyntaxNode> parentsAndSelf = syntaxNode.AncestorsAndSelf().Where(node => node is TypeDeclarationSyntax || node is NamespaceDeclarationSyntax);
StringBuilder stringBuilder = new StringBuilder();
bool typeEncountered = false;
bool anyEncountered = false;
foreach (SyntaxNode nameBuilderNode in parentsAndSelf.Reverse())
{
switch (nameBuilderNode)
{
case TypeDeclarationSyntax typeSyntax:
if (typeEncountered) stringBuilder.Append("+");
else if (anyEncountered) stringBuilder.Append(".");
// Appending the type name
stringBuilder.Append(typeSyntax.Identifier.ValueText);
typeEncountered = true;
break;
case NamespaceDeclarationSyntax namespaceSyntax:
if (anyEncountered) stringBuilder.Append(".");
// appending the namespace name
stringBuilder.Append(namespaceSyntax.Name.GetText());
break;
default:
break;
}
anyEncountered = true;
}
// The carriage return can sometimes end up here. Need to remove it.
string className = stringBuilder.ToString().Replace("
", “”);
// We have to do this check for partial classes.
if (!ClassNameToFileMapping.ContainsKey(className))
{
ClassNameToFileMapping.Add(className, cSharpFile);
}
}
}
}
}
/// <summary>
/// Get a file for a type.
/// </summary>
public static string GetFileForType(Type type)
{
return ClassNameToFileMapping[type.FullName];
}
public static string GetAssetPathForType(Type type)
{
string filePath = GetFileForType(type);
string assetPath = "Assets" + filePath.Substring(Application.dataPath.Length);
return assetPath;
}
}