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
;
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
;
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();
42 if (type
== myEolTokenType
) {
43 // Handle variant with several EOLs
44 if (startLineMarker
== null){
45 startLineMarker
= builder
.mark();
52 if (type
== myIndentTokenType
){
53 //noinspection ConstantConditions
54 currentIndent
= builder
.getTokenText().length();
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()));
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();