Appearance
A folding builder identifies the folding regions in the code. In this step of the tutorial, the folding builder is used to identify folding regions and replace the regions with specific text. Rather than the usual practice of using a folding builder to collapse a class, method, or comments to fewer lines, the folding builder replaces Simple Language keys with their corresponding values.
12.1. Define a Folding Builder
The SimpleFoldingBuilder replaces usages of properties with their values by default. Start by subclassing FoldingBuilderEx
Note that SimpleFoldingBuilder also implements DumbAware, which means the class is allowed to run in dumb mode, when indices are in background update.
INFO
A folding builder must implement DumbAware to function in this tutorial and pass tests.
The buildFoldRegions() method searches down a PSI tree from root to find all literal expressions containing the simple prefix simple:. The remainder of such a string is expected to contain a Simple Language key, and so the text range is stored as a FoldingDescriptor.
The getPlaceholderText() method retrieves the Simple Language value corresponding to the key associated with the (ASTNode) provided. The Consulo uses the value to substitute for the key when the code gets folded.
java
package org.consulo.sdk.language;
import consulo.application.dumb.DumbAware;
import consulo.codeEditor.FoldingGroup;
import consulo.document.Document;
import consulo.document.util.TextRange;
import consulo.language.ast.ASTNode;
import consulo.language.editor.folding.FoldingBuilderEx;
import consulo.language.editor.folding.FoldingDescriptor;
import consulo.language.psi.PsiElement;
import consulo.language.psi.PsiLiteralExpression;
import consulo.language.psi.util.PsiLiteralUtil;
import consulo.project.Project;
import consulo.util.collection.ContainerUtil;
import consulo.util.lang.StringUtil;
import org.consulo.sdk.language.psi.SimpleProperty;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
final class SimpleFoldingBuilder extends FoldingBuilderEx implements DumbAware {
@Nonnull
@Override
public FoldingDescriptor[] buildFoldRegions(@Nonnull PsiElement root,
@Nonnull Document document,
boolean quick) {
FoldingGroup group = FoldingGroup.newGroup(SimpleAnnotator.SIMPLE_PREFIX_STR);
List<FoldingDescriptor> descriptors = new ArrayList<>();
root.accept(new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitLiteralExpression(@Nonnull PsiLiteralExpression literalExpression) {
super.visitLiteralExpression(literalExpression);
String value = PsiLiteralUtil.getStringLiteralContent(literalExpression);
if (value != null &&
value.startsWith(SimpleAnnotator.SIMPLE_PREFIX_STR + SimpleAnnotator.SIMPLE_SEPARATOR_STR)) {
Project project = literalExpression.getProject();
String key = value.substring(
SimpleAnnotator.SIMPLE_PREFIX_STR.length() + SimpleAnnotator.SIMPLE_SEPARATOR_STR.length()
);
SimpleProperty simpleProperty = ContainerUtil.getOnlyItem(SimpleUtil.findProperties(project, key));
if (simpleProperty != null) {
descriptors.add(new FoldingDescriptor(literalExpression.getNode(),
new TextRange(literalExpression.getTextRange().getStartOffset() + 1,
literalExpression.getTextRange().getEndOffset() - 1),
group, Collections.singleton(simpleProperty)));
}
}
}
});
return descriptors.toArray(FoldingDescriptor.EMPTY_ARRAY);
}
@Nullable
@Override
public String getPlaceholderText(@Nonnull ASTNode node) {
if (node.getPsi() instanceof PsiLiteralExpression psiLiteralExpression) {
String text = PsiLiteralUtil.getStringLiteralContent(psiLiteralExpression);
if (text == null) {
return null;
}
String key = text.substring(SimpleAnnotator.SIMPLE_PREFIX_STR.length() +
SimpleAnnotator.SIMPLE_SEPARATOR_STR.length());
SimpleProperty simpleProperty = ContainerUtil.getOnlyItem(
SimpleUtil.findProperties(psiLiteralExpression.getProject(), key)
);
if (simpleProperty == null) {
return StringUtil.THREE_DOTS;
}
String propertyValue = simpleProperty.getValue();
if (propertyValue == null) {
return StringUtil.THREE_DOTS;
}
return propertyValue
.replaceAll("\n", "\\n")
.replaceAll("\"", "\\\\\"");
}
return null;
}
@Override
public boolean isCollapsedByDefault(@Nonnull ASTNode node) {
return true;
}
}12.2. Register the Folding Builder
The SimpleFoldingBuilder implementation is registered with the Consulo by annotating the class with @ExtensionImpl. The base class FoldingBuilderEx is annotated with @ExtensionAPI, so the Consulo discovers the implementation automatically.
12.3. Run the Project
Rebuild the project, and run simple_language_plugin in a Development Instance. Now when a Java file is opened in the Editor, it shows the property's value instead of the key. This is because SimpleFoldingBuilder.isCollapsedByDefault() always returns true. Try using Code | Folding | Expand All to show the key rather than the value.
