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 TypesToReplaceWithChildren { get; } = new HashSet(); /// /// 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. /// private static void RegisterType(int id) where TPsi : ITreeNode where TDeclared: IDeclaredElement { KnownPsiTypes.Add((typeof(TPsi), id)); KnownDeclaredTypes.Add((typeof(TDeclared), id)); } /// /// Same as , /// but for PSI nodes that have no corresponding declared elements. /// /// /// private static void RegisterType(int id) where TPsi : ITreeNode => KnownPsiTypes.Add((typeof(TPsi), id)); /// /// 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") /// private static void ReplaceWithChildren() => TypesToReplaceWithChildren.Add(typeof(TType)); static PsiElementRegistrar() { RegisterType(1); RegisterType(ClassDeclarationId); RegisterType(3); RegisterType(4); RegisterType(5); RegisterType(6); RegisterType(7); ReplaceWithChildren(); ReplaceWithChildren(); ReplaceWithChildren(); ReplaceWithChildren(); ReplaceWithChildren(); ReplaceWithChildren(); } 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)); } }