87 lines
2.9 KiB
C#
87 lines
2.9 KiB
C#
using Microsoft.CodeAnalysis;
|
|
using Microsoft.CodeAnalysis.CSharp;
|
|
using ArtificersScrollwork.Sidecar.Models;
|
|
|
|
namespace ArtificersScrollwork.Sidecar.Parsing;
|
|
|
|
/// <summary>
|
|
/// Walks a ServUO Scripts directory, parses all .cs files with Roslyn,
|
|
/// and builds a searchable in-memory index of classes and methods.
|
|
/// </summary>
|
|
public class ScriptIndexer
|
|
{
|
|
private readonly Dictionary<string, ClassInfo> _classes =
|
|
new(StringComparer.OrdinalIgnoreCase);
|
|
|
|
/// <summary>The Roslyn compilation built during indexing. Used by CallChainTracer.</summary>
|
|
public CSharpCompilation? Compilation { get; private set; }
|
|
|
|
public ScriptIndex IndexDirectory(string path)
|
|
{
|
|
var files = Directory
|
|
.EnumerateFiles(path, "*.cs", SearchOption.AllDirectories)
|
|
.ToList();
|
|
|
|
var syntaxTrees = files
|
|
.Select(f =>
|
|
{
|
|
try { return CSharpSyntaxTree.ParseText(File.ReadAllText(f), path: f); }
|
|
catch { return null; }
|
|
})
|
|
.Where(t => t is not null)
|
|
.Select(t => t!)
|
|
.ToList();
|
|
|
|
Compilation = CSharpCompilation.Create(
|
|
"ServUOAnalysis",
|
|
syntaxTrees,
|
|
references: null,
|
|
options: new CSharpCompilationOptions(
|
|
OutputKind.DynamicallyLinkedLibrary,
|
|
reportSuppressedDiagnostics: false
|
|
)
|
|
);
|
|
|
|
_classes.Clear();
|
|
|
|
foreach (var tree in syntaxTrees)
|
|
{
|
|
SemanticModel model;
|
|
try { model = Compilation.GetSemanticModel(tree); }
|
|
catch { continue; }
|
|
|
|
var walker = new ClassWalker(model, tree.FilePath);
|
|
try { walker.Visit(tree.GetRoot()); }
|
|
catch { continue; }
|
|
|
|
foreach (var cls in walker.Classes)
|
|
_classes[cls.Name] = cls;
|
|
}
|
|
|
|
var summaries = _classes.Values
|
|
.Select(c => new ClassSummary(
|
|
c.Name, c.Namespace, c.FilePath, c.BaseClass,
|
|
c.IsGump, c.IsMobile, c.IsItem))
|
|
.OrderBy(c => c.Namespace)
|
|
.ThenBy(c => c.Name)
|
|
.ToList();
|
|
|
|
return new ScriptIndex(_classes.Count, files.Count, summaries);
|
|
}
|
|
|
|
public ClassInfo? GetClass(string name) =>
|
|
_classes.TryGetValue(name, out var cls) ? cls : null;
|
|
|
|
public List<ClassSummary> Search(string query) =>
|
|
_classes.Values
|
|
.Where(c =>
|
|
c.Name.Contains(query, StringComparison.OrdinalIgnoreCase) ||
|
|
c.Namespace.Contains(query, StringComparison.OrdinalIgnoreCase) ||
|
|
c.Methods.Any(m => m.Name.Contains(query, StringComparison.OrdinalIgnoreCase)))
|
|
.Select(c => new ClassSummary(
|
|
c.Name, c.Namespace, c.FilePath, c.BaseClass,
|
|
c.IsGump, c.IsMobile, c.IsItem))
|
|
.Take(100)
|
|
.ToList();
|
|
}
|