package org.jetbrains.plugins.clojure.formatter;
import com.intellij.formatting.Alignment;
import com.intellij.formatting.Block;
import com.intellij.formatting.Indent;
import com.intellij.formatting.Wrap;
import com.intellij.lang.ASTNode;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiComment;
import com.intellij.psi.tree.TokenSet;
import com.intellij.psi.impl.source.tree.LeafPsiElement;
import org.jetbrains.plugins.clojure.formatter.processors.ClojureIndentProcessor;
import org.jetbrains.plugins.clojure.formatter.codeStyle.ClojureCodeStyleSettings;
import org.jetbrains.plugins.clojure.psi.api.*;
import org.jetbrains.plugins.clojure.psi.api.defs.ClDef;
import org.jetbrains.plugins.clojure.psi.api.symbols.ClSymbol;
import org.jetbrains.plugins.clojure.lexer.ClojureTokenTypes;
import org.jetbrains.plugins.clojure.psi.util.ClojurePsiCheckers;
import java.util.ArrayList;
import java.util.List;
/**
* @author ilyas
*/
public class ClojureBlockGenerator {
private static final TokenSet RIGHT_BRACES = TokenSet.create(ClojureTokenTypes.RIGHT_CURLY, ClojureTokenTypes.RIGHT_SQUARE);
private ASTNode myNode;
private Wrap myWrap;
private CodeStyleSettings mySettings;
private ClojureBlock myBlock;
public List<Block> generateSubBlocks(ASTNode node, Wrap wrap, CodeStyleSettings settings, ClojureBlock block) {
myNode = node;
myWrap = wrap;
mySettings = settings;
myBlock = block;
PsiElement blockPsi = myBlock.getNode().getPsi();
final ArrayList<Block> subBlocks = new ArrayList<Block>();
ASTNode children[] = myNode.getChildren(null);
ASTNode prevChildNode = null;
final ClojureCodeStyleSettings clSettings = block.getSettings().getCustomSettings(ClojureCodeStyleSettings.class);
for (ASTNode childNode : children) {
if (canBeCorrectBlock(childNode)) {
final PsiElement childPsi = childNode.getPsi();
final boolean mustAlign = mustAlign(blockPsi, childPsi, clSettings);
final Indent indent = ClojureIndentProcessor.getChildIndent(myBlock, prevChildNode, childNode);
subBlocks.add(new ClojureBlock(childNode,
mustAlign ? block.childAlignment : Alignment.createAlignment(),
indent, myWrap, mySettings));
prevChildNode = childNode;
}
}
return subBlocks;
}
public static boolean mustAlign(PsiElement blockPsi, PsiElement child, ClojureCodeStyleSettings settings) {
if (blockPsi instanceof ClVector || blockPsi instanceof ClMap) {
return !(child instanceof LeafPsiElement) || RIGHT_BRACES.contains(child.getNode().getElementType()) ||
(child instanceof PsiComment);
}
if (blockPsi instanceof ClList &&
!(blockPsi instanceof ClDef)) {
final ClList list = (ClList) blockPsi;
PsiElement first = list.getFirstNonLeafElement();
if (settings.ALIGN_CLOJURE_FORMS || ClojurePsiCheckers.isImportMember(list)) {
if (first == child && !applicationStart(first)) return true;
if (first != null &&
!applicationStart(first) &&
first.getTextRange().getEndOffset() <= child.getTextRange().getStartOffset()) {
return true;
}
final PsiElement second = list.getSecondNonLeafElement();
if (second != null && child != null &&
second.getTextRange().getEndOffset() <= child.getTextRange().getStartOffset()) {
return true;
}
}
// CLJ-98
if (first instanceof ClKeyword && child != null &&
first.getTextRange().getEndOffset() <= child.getTextRange().getStartOffset()) {
return true;
}
}
if (blockPsi instanceof ClLiteral) {
ASTNode node = blockPsi.getNode();
assert node != null;
ASTNode[] elements = node.getChildren(null);
if (elements.length > 0 && elements[0].getElementType() == ClojureTokenTypes.STRING_LITERAL) {
return true;
}
}
return false;
}
private static boolean applicationStart(PsiElement first) {
return first instanceof ClSymbol;
}
private static boolean canBeCorrectBlock(final ASTNode node) {
return (node.getText().trim().length() > 0);
}
}