public static @NotNull List<TextContent> findTextsAt(@NotNull PsiElement psi, @NotNull Set<TextContent.TextDomain> allowedDomains) {
    TextRange psiRange = psi.getTextRange();
    PsiFile file = null;
    for (PsiElement each = psi; each != null; each = each.getParent()) {
      CachedValue<Cache> cache = each.getUserData(COMMON_PARENT_CACHE);
      if (cache != null) {
        List<TextContent> cached = ContainerUtil.filter(cache.getValue().getCached(allowedDomains), c ->
          c.getUserData(IGNORED) == null && c.intersectsRange(psiRange));
        if (!cached.isEmpty()) {
          return cached;
        }
      }
      if (each instanceof PsiFile) {
        file = (PsiFile)each;
        break;
      }
    }

    Language fileLanguage = (file != null ? file : psi.getContainingFile()).getLanguage();

    for (PsiElement each = psi; each != null; each = each.getParent()) {
      RecursionGuard.StackStamp stamp = RecursionManager.markStack();

      List<TextContent> contents = obtainContents(allowedDomains, fileLanguage, each);
      if (stamp.mayCacheNow() && !contents.isEmpty()) {
        StreamEx.of(contents)
          .groupingBy(TextContent::getCommonParent)
          .forEach((commonParent, group) -> obtainCache(commonParent, COMMON_PARENT_CACHE).register(allowedDomains, group));
      }

      if (!contents.isEmpty()) {
        return ContainerUtil.filter(contents, c ->
          c.getUserData(IGNORED) == null && allowedDomains.contains(c.getDomain()) && c.intersectsRange(psiRange));
      }
    }

    return Collections.emptyList();
  }
public static @NotNull List<TextContent> findTextsAt(@NotNull PsiElement psi, @NotNull Set<TextContent.TextDomain> allowedDomains)