package org.jetbrains.plugins.clojure.formatter.processors;
import com.intellij.formatting.Block;
import com.intellij.formatting.Spacing;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.source.tree.LeafPsiElement;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import org.jetbrains.plugins.clojure.formatter.ClojureBlock;
import org.jetbrains.plugins.clojure.lexer.TokenSets;
import org.jetbrains.plugins.clojure.parser.ClojureElementTypes;
import org.jetbrains.plugins.clojure.lexer.ClojureTokenTypes;
import org.jetbrains.plugins.clojure.psi.api.ClKeyword;
import org.jetbrains.plugins.clojure.psi.api.ClListLike;
import org.jetbrains.plugins.clojure.psi.util.ClojurePsiCheckers;
/**
* @author ilyas
*/
public class ClojureSpacingProcessor implements ClojureElementTypes {
private static final Spacing NO_SPACING = Spacing.createSpacing(0, 0, 0, false, 0);
private static final Spacing NO_SPACING_WITH_NEWLINE = Spacing.createSpacing(0, 0, 0, true, 1);
private static final Spacing MANDATORY_NEWLINE = Spacing.createSpacing(1, 1, 1, true, 100);
private static final Spacing NS_SPACING = Spacing.createSpacing(1, 1, 2, true, 100);
private static final Spacing COMMON_SPACING = Spacing.createSpacing(1, 1, 0, true, 100);
private static final Spacing NO_NEWLINE = Spacing.createSpacing(1, 1, 0, false, 0);
public static Spacing getSpacing(Block child1, Block child2) {
if (!(child1 instanceof ClojureBlock) || !(child2 instanceof ClojureBlock)) return null;
ClojureBlock block1 = (ClojureBlock) child1;
ClojureBlock block2 = (ClojureBlock) child2;
ASTNode node1 = block1.getNode();
ASTNode node2 = block2.getNode();
IElementType type1 = node1.getElementType();
IElementType type2 = node2.getElementType();
final Spacing psiBased = psiBasedSpacing(node1.getPsi(), node2.getPsi());
if (psiBased != null) {
return psiBased;
}
if (MODIFIERS.contains(type1)) {
return NO_SPACING;
}
if (ClojureTokenTypes.ATOMS.contains(type2)) {
return NO_SPACING;
}
String text1 = node1.getText();
String text2 = node2.getText();
if (text1.trim().startsWith(",") || text2.trim().startsWith(",")) {
return null;
}
if (BRACES.contains(type1) || BRACES.contains(type2)) {
return NO_SPACING_WITH_NEWLINE;
}
return COMMON_SPACING;
}
private static Spacing psiBasedSpacing(PsiElement psi1, PsiElement psi2) {
final IElementType rightElementType = psi2.getNode().getElementType();
// Namespace declaration
if (ClojurePsiCheckers.isNs(psi1)) {
return NS_SPACING;
}
if (ClojurePsiCheckers.isImportingClause(psi2)) {
return MANDATORY_NEWLINE;
}
// todo questionable: should be adjustable
if (psi1 instanceof ClKeyword) {
if (TokenSets.RIGHT_PARENTHESES.contains(rightElementType)) return null;
return NO_NEWLINE;
}
// formatting imports
if (psi1 instanceof ClListLike &&
psi2 instanceof ClListLike &&
psi1.getParent() == psi2.getParent() &&
ClojurePsiCheckers.isImportingClause(psi1.getParent())) {
return MANDATORY_NEWLINE;
}
// todo add more cases
return null;
}
}