blob: 457f64ac8713f6bd9109ac024a59c8fc91a4eb4e [file] [log] [blame]
/*
* Copyright 2007 Sascha Weinreuter
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.intellij.plugins.relaxNG.compact.folding;
import com.intellij.codeInsight.folding.CodeFoldingSettings;
import com.intellij.lang.ASTNode;
import com.intellij.lang.folding.FoldingBuilder;
import com.intellij.lang.folding.FoldingDescriptor;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import org.intellij.plugins.relaxNG.compact.RncElementTypes;
import org.intellij.plugins.relaxNG.compact.RncTokenTypes;
import org.intellij.plugins.relaxNG.compact.psi.RncAnnotation;
import org.intellij.plugins.relaxNG.compact.psi.RncName;
import org.intellij.plugins.relaxNG.compact.psi.util.EscapeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
/**
* Created by IntelliJ IDEA.
* User: sweinreuter
* Date: 10.08.2007
*/
public class RncFoldingBuilder implements FoldingBuilder {
@Override
@NotNull
public FoldingDescriptor[] buildFoldRegions(@NotNull ASTNode node, @NotNull Document document) {
final ArrayList<FoldingDescriptor> regions = new ArrayList<FoldingDescriptor>();
process(node, document, regions);
return regions.size() > 0
? regions.toArray(new FoldingDescriptor[regions.size()])
: FoldingDescriptor.EMPTY;
}
@Override
public String getPlaceholderText(@NotNull ASTNode node) {
final IElementType type = node.getElementType();
if (type == RncTokenTypes.LBRACE) {
return "{ ... }";
} else if (isCommentLike(type)) {
return "# ...";
} else if (isAnnotation(type)) {
final RncName element = ((RncAnnotation)node.getPsi()).getNameElement();
if (element != null) {
final ASTNode n = element.getNode();
assert n != null;
return EscapeUtil.unescapeText(n) + " [ ... ]";
}
return "[ ... ]";
} else {
return "...";
}
}
private static boolean isAnnotation(IElementType type) {
return RncElementTypes.ANNOTATION == type || RncElementTypes.ANNOTATION_ELEMENT == type || RncElementTypes.FORWARD_ANNOTATION == type;
}
private static boolean isCommentLike(IElementType type) {
return RncTokenTypes.COMMENTS.contains(type) || RncTokenTypes.DOC_TOKENS.contains(type);
}
@Override
public boolean isCollapsedByDefault(@NotNull ASTNode node) {
return isCommentLike(node.getElementType()) && CodeFoldingSettings.getInstance().COLLAPSE_DOC_COMMENTS;
}
private static void process(@Nullable ASTNode node, Document document, ArrayList<FoldingDescriptor> regions) {
if (node == null) {
return;
}
final ASTNode[] braces = node.getChildren(RncTokenTypes.BRACES);
if (braces.length == 2) {
final ASTNode lbrace = braces[0];
final ASTNode rbrace = braces[1];
if (shouldFold(lbrace, rbrace, document)) {
final TextRange range = new TextRange(lbrace.getStartOffset(), rbrace.getTextRange().getEndOffset());
regions.add(new FoldingDescriptor(lbrace, range));
}
} else if (isAnnotation(node.getElementType())) {
if (isOnDifferentLine(node.getFirstChildNode(), node.getLastChildNode(), document)) {
regions.add(new FoldingDescriptor(node, node.getTextRange()));
}
}
node = node.getFirstChildNode();
while (node != null) {
node = checkNodeAndSiblings(node, RncTokenTypes.DOC_TOKENS, regions, document);
node = checkNodeAndSiblings(node, RncTokenTypes.COMMENTS, regions, document);
process(node, document, regions);
if (node != null) {
node = node.getTreeNext();
}
}
}
@Nullable
private static ASTNode checkNodeAndSiblings(@Nullable ASTNode node, TokenSet tokens, ArrayList<FoldingDescriptor> regions, Document document) {
if (node != null && tokens.contains(node.getElementType())) {
final ASTNode start = node;
ASTNode end = start;
node = node.getTreeNext();
if (node != null) {
do {
end = node;
node = node.getTreeNext();
} while (node != null && tokens.contains(node.getElementType()));
}
if (end != start) {
while (end.getPsi() instanceof PsiWhiteSpace) {
end = end.getTreePrev();
}
if (isOnDifferentLine(start, end, document)) {
regions.add(new FoldingDescriptor(start, new TextRange(start.getStartOffset(), end.getTextRange().getEndOffset())));
}
}
}
return node;
}
private static boolean shouldFold(ASTNode first, ASTNode second, Document document) {
if (first.getElementType() != RncTokenTypes.LBRACE) {
return false;
} else if (second.getElementType() != RncTokenTypes.RBRACE) {
return false;
} else {
return isOnDifferentLine(first, second, document);
}
}
private static boolean isOnDifferentLine(ASTNode first, ASTNode second, Document document) {
return document.getLineNumber(first.getStartOffset()) != document.getLineNumber(second.getStartOffset());
}
}