EnvDTE.Host/Callback/Util/PsiElementRegistrar.cs (68 lines of code) (raw):
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.CSharp.Tree;
using JetBrains.ReSharper.Psi.Tree;
namespace JetBrains.EnvDTE.Host.Callback.Util
{
public static class PsiElementRegistrar
{
public const int ClassDeclarationId = 2;
private static IList<(Type, int)> KnownPsiTypes { get; } = new List<(Type, int)>();
private static IList<(Type, int)> KnownDeclaredTypes { get; } = new List<(Type, int)>();
private static ISet<Type> TypesToReplaceWithChildren { get; } = new HashSet<Type>();
/// <summary>
/// The type id for TType will be set to id.
/// The same id can later be used by the client
/// to deduce the type of EnvDTE AST node to create.
/// </summary>
private static void RegisterType<TPsi, TDeclared>(int id)
where TPsi : ITreeNode
where TDeclared: IDeclaredElement
{
KnownPsiTypes.Add((typeof(TPsi), id));
KnownDeclaredTypes.Add((typeof(TDeclared), id));
}
/// <summary>
/// Same as <see cref="RegisterType{TPsi,TDeclared}(int)"/>,
/// but for PSI nodes that have no corresponding declared elements.
/// </summary>
/// <param name="id"></param>
/// <typeparam name="TPsi"></typeparam>
private static void RegisterType<TPsi>(int id) where TPsi : ITreeNode =>
KnownPsiTypes.Add((typeof(TPsi), id));
/// <summary>
/// Elements that have not been registered are ignored by default.
/// However, it is sometimes interesting to replace the entire element with its children.
/// That is the case, for example, for namespaces and classes. The PSI structure of them is the following:
/// INamespaceDeclaration("ConsoleApplication1")
/// INamespaceBody
/// IClassDeclaration("Class1") (?)
/// IClassDeclaration("Class2")
/// The EnvDTE structure of them is the following:
/// CodeNamespace("ConsoleApplication1")
/// CodeClass("Class1")
/// CodeClass("Class2")
/// </summary>
private static void ReplaceWithChildren<TType>() => TypesToReplaceWithChildren.Add(typeof(TType));
static PsiElementRegistrar()
{
RegisterType<ICSharpNamespaceDeclaration, INamespace>(1);
RegisterType<IClassDeclaration, IClass>(ClassDeclarationId);
RegisterType<IStructDeclaration, IStruct>(3);
RegisterType<IInterfaceDeclaration, IInterface>(4);
RegisterType<IFunctionDeclaration, IFunction>(5);
RegisterType<IParameterDeclaration, IParameter>(6);
RegisterType<IAttribute>(7);
ReplaceWithChildren<INamespaceBody>();
ReplaceWithChildren<IClassBody>();
ReplaceWithChildren<IFormalParameterList>();
ReplaceWithChildren<IAttributeSection>();
ReplaceWithChildren<IAttributeSectionList>();
ReplaceWithChildren<IAttributeList>();
}
public static int GetTypeId([NotNull] ITreeNode node)
{
foreach (var (type, id) in KnownPsiTypes)
{
if (type.IsInstanceOfType(node))
{
return id;
}
}
return 0;
}
public static int GetTypeId(IDeclaredElement element)
{
foreach (var (type, id) in KnownDeclaredTypes)
{
if (type.IsInstanceOfType(element))
{
return id;
}
}
return 0;
}
public static bool ShouldAddToModel([NotNull] ITreeNode node) => GetTypeId(node) != 0;
public static bool ShouldVisitChildren([NotNull] ITreeNode node) =>
GetTypeId(node) != 0 || TypesToReplaceWithChildren.Any(it => it.IsInstanceOfType(node));
}
}