NPE (18686)
[fedora-idea.git] / platform / lang-impl / src / com / intellij / indentation / IndentationParser.java
blob9f488dd09184dd09ba246a02df664e89ec43f047
1 package com.intellij.indentation;
3 import com.intellij.lang.ASTNode;
4 import com.intellij.lang.PsiBuilder;
5 import com.intellij.lang.PsiParser;
6 import com.intellij.openapi.util.Pair;
7 import com.intellij.psi.tree.IElementType;
8 import org.jetbrains.annotations.NotNull;
10 import java.util.Stack;
12 /**
13 * @author oleg
15 public abstract class IndentationParser implements PsiParser {
16 private final IElementType myEolTokenType;
17 private final IElementType myIndentTokenType;
18 private final IElementType myBlockElementType;
20 public IndentationParser(final IElementType blockElementType,
21 final IElementType eolTokenType,
22 final IElementType indentTokenType) {
23 myBlockElementType = blockElementType;
24 myEolTokenType = eolTokenType;
25 myIndentTokenType = indentTokenType;
28 @NotNull
29 public ASTNode parse(final IElementType root, final PsiBuilder builder) {
30 final PsiBuilder.Marker fileMarker = builder.mark();
32 final Stack<Pair<Integer, PsiBuilder.Marker>> stack = new Stack<Pair<Integer, PsiBuilder.Marker>>();
33 stack.push(Pair.create(0, builder.mark()));
35 PsiBuilder.Marker startLineMarker = null;
36 int currentIndent = 0;
37 boolean eolSeen = false;
39 while (!builder.eof()) {
40 final IElementType type = builder.getTokenType();
41 // EOL
42 if (type == myEolTokenType) {
43 // Handle variant with several EOLs
44 if (startLineMarker == null){
45 startLineMarker = builder.mark();
47 eolSeen = true;
48 } else
50 // Indent
52 if (type == myIndentTokenType){
53 //noinspection ConstantConditions
54 currentIndent = builder.getTokenText().length();
55 } else
57 if (eolSeen) {
58 if (startLineMarker != null){
59 startLineMarker.rollbackTo();
60 startLineMarker = null;
62 // Close indentation blocks
63 while (!stack.isEmpty() && currentIndent < stack.peek().first){
64 stack.pop().second.done(myBlockElementType);
67 if (!stack.isEmpty()) {
68 final Pair<Integer, PsiBuilder.Marker> pair = stack.peek();
69 if (currentIndent == pair.first) {
70 stack.pop().second.done(myBlockElementType);
71 passEOLsAndIndents(builder);
72 stack.push(Pair.create(currentIndent, builder.mark()));
74 if (currentIndent > pair.first) {
75 passEOLsAndIndents(builder);
76 stack.push(Pair.create(currentIndent, builder.mark()));
79 eolSeen = false;
80 currentIndent = 0;
83 builder.advanceLexer();
86 // Close all left opened markers
87 if (startLineMarker != null){
88 startLineMarker.drop();
90 while (!stack.isEmpty()){
91 stack.pop().second.done(myBlockElementType);
94 fileMarker.done(root);
95 return builder.getTreeBuilt();
98 private void passEOLsAndIndents(final PsiBuilder builder) {
99 IElementType tokenType = builder.getTokenType();
100 while (tokenType == myEolTokenType || tokenType == myIndentTokenType){
101 builder.advanceLexer();
102 tokenType = builder.getTokenType();