using System; using JetBrains.Annotations; using JetBrains.Application.Parts; using JetBrains.ProjectModel; using JetBrains.ReSharper.Psi; using JetBrains.ReSharper.Psi.Tree; namespace JetBrains.EnvDTE.Host.Manager { /// /// Maintains the ids to be used in protocol AST /// [SolutionComponent(Instantiation.DemandAnyThreadSafe)] public class AstManager { [NotNull] private AstContainer DetachedAstContainer { get; } [NotNull] private AstContainer PsiContainer { get; } [NotNull] private AstContainer TypeContainer { get; } public AstManager() { var source = new IdSource(); DetachedAstContainer = new AstContainer(source); PsiContainer = new AstContainer(source); TypeContainer = new AstContainer(source); } [NotNull] public TResult MapElement( int id, [NotNull] Func psiMapper, [NotNull] Func declaredElementMapper, [NotNull] Func typeMapper ) where TNode : ITreeNode where TElement : IDeclaredElement => TryMapWith(id, PsiContainer, psiMapper) ?? TryMapWith(id, DetachedAstContainer, declaredElementMapper) ?? TryMapWith(id, TypeContainer, typeMapper) ?? throw new InvalidOperationException($"Attempted to map non-existing element with id {id}"); [CanBeNull] private static TResult TryMapWith( int id, [NotNull] AstContainer container, [NotNull] Func mapper ) where TNode : TBaseNode where TBaseNode : class { var node = container.TryGetElement(id); if (node == null) return default; if (node is TNode correct) return mapper(correct); throw new InvalidOperationException($"Wrong node type. Expected {typeof(TNode)}, actual {node.GetType()}"); } public int GetOrCreateId([NotNull] ITreeNode node) => PsiContainer.GetOrCreateId(node); public int GetOrCreateId([NotNull] IDeclaredElement element) => DetachedAstContainer.GetOrCreateId(element); public int GetOrCreateId([NotNull] IArrayType type) => TypeContainer.GetOrCreateId(type); } }