NPE (18686)
[fedora-idea.git] / platform / lang-impl / src / com / intellij / indentation / IndentationAnnotatorVisitor.java
blob937198ab81e89fda74ba97fc910d9a11bf1631ce
1 package com.intellij.indentation;
3 import com.intellij.lang.annotation.AnnotationHolder;
4 import com.intellij.psi.PsiElement;
5 import com.intellij.psi.PsiElementVisitor;
6 import com.intellij.psi.PsiFile;
7 import com.intellij.psi.tree.IElementType;
8 import com.intellij.psi.util.PsiTreeUtil;
9 import org.jetbrains.annotations.NotNull;
11 /**
12 * @author oleg
14 public class IndentationAnnotatorVisitor extends PsiElementVisitor {
15 protected final AnnotationHolder myHolder;
16 private final IElementType myIndentTokenType;
17 private final IElementType myEolTokenType;
19 public IndentationAnnotatorVisitor(@NotNull final AnnotationHolder holder,
20 final IElementType indentTokenType,
21 final IElementType eolTokenType) {
22 myHolder = holder;
23 myIndentTokenType = indentTokenType;
24 myEolTokenType = eolTokenType;
27 @SuppressWarnings({"ConstantConditions"})
28 @Override
29 public void visitFile(final PsiFile file) {
30 int lastIndent = 0;
31 IndentInfo indentInfo = null;
32 PsiElement leaf = PsiTreeUtil.getDeepestFirst(file);
33 if (leaf == null || leaf instanceof PsiFile){
34 return;
36 PsiElement nextLeaf = PsiTreeUtil.nextLeaf(leaf);
38 // First leaf check
39 if (leaf.getNode().getElementType() == myIndentTokenType){
40 myHolder.createErrorAnnotation(leaf, "Indenting at the beginning of the document is illegal");
42 leaf = nextLeaf;
43 nextLeaf = leaf != null ? PsiTreeUtil.nextLeaf(leaf) : null;
45 // Iterating over leafs
46 while (leaf != null) {
47 final IElementType leafType = leaf.getNode().getElementType();
48 if (leafType == myIndentTokenType && nextLeaf!=null && nextLeaf.getNode().getElementType() != myEolTokenType){
49 final String currentIndentText = leaf.getText();
50 final IndentInfo currentIndent = getIndent(currentIndentText);
51 final int currentIndentLength = currentIndent.length;
52 // Check if spaces and tabs are mixed
53 if (currentIndentLength == -1){
54 myHolder.createErrorAnnotation(leaf, "Indentation can't use both tabs and spaces");
56 else
57 if (currentIndentLength > 0) {
58 // If we don't have indent info registered
59 if (indentInfo == null){
60 indentInfo = currentIndent;
61 lastIndent = currentIndentLength;
62 } else {
63 final Boolean useTab = indentInfo.useTab;
64 final int indentInfoLength = indentInfo.length;
65 // If indent and current indent use different space and tabs
66 if (useTab && currentIndentText.contains(" ") || !useTab && currentIndentText.contains("\t")) {
67 final String message = useTab
68 ? "Inconsistent indentation: " + currentIndentLength +
69 " spaces were used for indentation, but the rest of the document was indented using " +
70 indentInfoLength + " tabs"
71 : "Inconsistent indentation: " + currentIndentLength +
72 " tabs were used for indentation, but the rest of the document was indented using " +
73 indentInfoLength + " spaces";
74 myHolder.createErrorAnnotation(leaf, message);
75 } else {
76 // Check indent length
77 final int delta = currentIndentLength - lastIndent;
78 if (currentIndentLength % indentInfoLength != 0 || delta > indentInfoLength) {
79 final String message = useTab
80 ? currentIndentLength + " tabs were used for indentation. Must be indented using " + indentInfoLength + " tabs"
81 : currentIndentLength + " spaces were used for indentation. Must be indented using " + indentInfoLength + " spaces";
82 myHolder.createErrorAnnotation(leaf, message);
84 lastIndent = currentIndentLength / indentInfoLength * indentInfoLength;
89 leaf = nextLeaf;
90 nextLeaf = leaf != null ? PsiTreeUtil.nextLeaf(leaf) : null;
95 private class IndentInfo {
96 final int length;
97 final boolean useTab;
99 private IndentInfo(final int length, final boolean useTab) {
100 this.length = length;
101 this.useTab = useTab;
106 private IndentInfo getIndent(final String text) {
107 if (text.length() == 0){
108 return new IndentInfo(0, false);
110 if (text.contains("\t ") || text.contains(" \t")){
111 return new IndentInfo(-1, false);
113 return new IndentInfo(text.length(), text.contains("\t"));