<commit_before>// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.lang.folding;

import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.PossiblyDumbAware;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.registry.RegistryValue;
import com.intellij.psi.PsiComment;
import com.intellij.psi.PsiElement;
import com.intellij.util.containers.Stack;
import com.intellij.util.containers.hash.HashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * Builds custom folding regions. If custom folding is supported for a language, its FoldingBuilder must be inherited from this class.
 *
 * @author <NAME>
 */
public abstract class CustomFoldingBuilder extends FoldingBuilderEx implements PossiblyDumbAware {
  private CustomFoldingProvider myDefaultProvider;
  private static final RegistryValue myMaxLookupDepth = Registry.get("custom.folding.max.lookup.depth");
  private static final ThreadLocal<Set<ASTNode>> ourCustomRegionElements = new ThreadLocal<>();

  @NotNull
  @Override
  public final FoldingDescriptor[] buildFoldRegions(@NotNull PsiElement root, @NotNull Document document, boolean quick) {
    ourCustomRegionElements.set(new HashSet<>());
    List<FoldingDescriptor> descriptors = new ArrayList<>();
    try {
      if (CustomFoldingProvider.getAllProviders().size() > 0) {
        myDefaultProvider = null;
        ASTNode rootNode = root.getNode();
        if (rootNode != null) {
          addCustomFoldingRegionsRecursively(new FoldingStack(rootNode), rootNode, descriptors, 0);
        }
      }
      buildLanguageFoldRegions(descriptors, root, document, quick);
    }
    finally {
      ourCustomRegionElements.set(null);
    }
    return descriptors.toArray(FoldingDescriptor.EMPTY);
  }

  @NotNull
  @Override
  public final FoldingDescriptor[] buildFoldRegions(@NotNull ASTNode node, @NotNull Document document) {
    return buildFoldRegions(node.getPsi(), document, false);
  }

  /**
   * Implement this method to build language folding regions besides custom folding regions.
   *
   * @param descriptors The list of folding descriptors to store results to.
   * @param root        The root node for which the folding is requested.
   * @param document    The document for which folding is built.
   * @param quick       whether the result should be provided as soon as possible without reference resolving
   *                    and complex checks.
   */
  protected abstract void buildLanguageFoldRegions(@NotNull List<FoldingDescriptor> descriptors,
                                                   @NotNull PsiElement root,
                                                   @NotNull Document document,
                                                   boolean quick);

  private void addCustomFoldingRegionsRecursively(@NotNull FoldingStack foldingStack,
                                                  @NotNull ASTNode node,
                                                  @NotNull List<? super FoldingDescriptor> descriptors,
                                                  int currDepth) {
    FoldingStack localFoldingStack = isCustomFoldingRoot(node) ? new FoldingStack(node) : foldingStack;
    for (ASTNode child = node.getFirstChildNode(); child != null; child = child.getTreeNext()) {
      if (isCustomRegionStart(child)) {
        localFoldingStack.push(child);
      }
      else if (isCustomRegionEnd(child)) {
        if (!localFoldingStack.isEmpty()) {
          ASTNode startNode = localFoldingStack.pop();
          int startOffset = startNode.getTextRange().getStartOffset();
          TextRange range = new TextRange(startOffset, child.getTextRange().getEndOffset());
          startNode.getPsi().putUserData(CompositeFoldingBuilder.FOLDING_BUILDER, this);
          descriptors.add(new FoldingDescriptor(startNode, range));
          Set<ASTNode> nodeSet = ourCustomRegionElements.get();
          nodeSet.add(startNode);
          nodeSet.add(child);
        }
      }
      else {
        if (currDepth < myMaxLookupDepth.asInteger()) {
          addCustomFoldingRegionsRecursively(localFoldingStack, child, descriptors, currDepth + 1);
        }
      }
    }
  }

  @Override
  public final String getPlaceholderText(@NotNull ASTNode node, @NotNull TextRange range) {
    if (isCustomFoldingCandidate(node)) {
      String elementText = node.getText();<commit_msg>js: improve folding performance (insert checkCanceled(), better method for text matching)<commit_after>// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.lang.folding;

import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.PossiblyDumbAware;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.registry.RegistryValue;
import com.intellij.psi.PsiComment;
import com.intellij.psi.PsiElement;
import com.intellij.util.containers.Stack;
import com.intellij.util.containers.hash.HashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * Builds custom folding regions. If custom folding is supported for a language, its FoldingBuilder must be inherited from this class.
 *
 * @author <NAME>
 */
public abstract class CustomFoldingBuilder extends FoldingBuilderEx implements PossiblyDumbAware {
  private CustomFoldingProvider myDefaultProvider;
  private static final RegistryValue myMaxLookupDepth = Registry.get("custom.folding.max.lookup.depth");
  private static final ThreadLocal<Set<ASTNode>> ourCustomRegionElements = new ThreadLocal<>();

  @NotNull
  @Override
  public final FoldingDescriptor[] buildFoldRegions(@NotNull PsiElement root, @NotNull Document document, boolean quick) {
    ourCustomRegionElements.set(new HashSet<>());
    List<FoldingDescriptor> descriptors = new ArrayList<>();
    try {
      if (CustomFoldingProvider.getAllProviders().size() > 0) {
        myDefaultProvider = null;
        ASTNode rootNode = root.getNode();
        if (rootNode != null) {
          addCustomFoldingRegionsRecursively(new FoldingStack(rootNode), rootNode, descriptors, 0);
        }
      }
      buildLanguageFoldRegions(descriptors, root, document, quick);
    }
    finally {
      ourCustomRegionElements.set(null);
    }
    return descriptors.toArray(FoldingDescriptor.EMPTY);
  }

  @NotNull
  @Override
  public final FoldingDescriptor[] buildFoldRegions(@NotNull ASTNode node, @NotNull Document document) {
    return buildFoldRegions(node.getPsi(), document, false);
  }

  /**
   * Implement this method to build language folding regions besides custom folding regions.
   *
   * @param descriptors The list of folding descriptors to store results to.
   * @param root        The root node for which the folding is requested.
   * @param document    The document for which folding is built.
   * @param quick       whether the result should be provided as soon as possible without reference resolving
   *                    and complex checks.
   */
  protected abstract void buildLanguageFoldRegions(@NotNull List<FoldingDescriptor> descriptors,
                                                   @NotNull PsiElement root,
                                                   @NotNull Document document,
                                                   boolean quick);

  private void addCustomFoldingRegionsRecursively(@NotNull FoldingStack foldingStack,
                                                  @NotNull ASTNode node,
                                                  @NotNull List<? super FoldingDescriptor> descriptors,
                                                  int currDepth) {
    FoldingStack localFoldingStack = isCustomFoldingRoot(node) ? new FoldingStack(node) : foldingStack;
    for (ASTNode child = node.getFirstChildNode(); child != null; child = child.getTreeNext()) {
      ProgressManager.checkCanceled();
      
      if (isCustomRegionStart(child)) {
        localFoldingStack.push(child);
      }
      else if (isCustomRegionEnd(child)) {
        if (!localFoldingStack.isEmpty()) {
          ASTNode startNode = localFoldingStack.pop();
          int startOffset = startNode.getTextRange().getStartOffset();
          TextRange range = new TextRange(startOffset, child.getTextRange().getEndOffset());
          startNode.getPsi().putUserData(CompositeFoldingBuilder.FOLDING_BUILDER, this);
          descriptors.add(new FoldingDescriptor(startNode, range));
          Set<ASTNode> nodeSet = ourCustomRegionElements.get();
          nodeSet.add(startNode);
          nodeSet.add(child);
        }
      }
      else {
        if (currDepth < myMaxLookupDepth.asInteger()) {
          addCustomFoldingRegionsRecursively(localFoldingStack, child, descriptors, currDepth + 1);
        }
      }
    }
  }

  @Override
  public final String getPlaceholderText(@NotNull ASTNode node, @NotNull TextRange range) {
    if (isCustomFoldingCandidate(node)) {
      String elementText = node.getText();
{'fraction_non_alphanumeric': 0.08498850094083212, 'fraction_numerical': 0.002717959439682208, 'mean_word_length': 2.9995819397993313, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 24, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2563347', 'n_tokens_mistral': 2511, 'n_tokens_neox': 2438, 'n_words': 650}