ASTNode (final IElementType root, final PsiBuilder builder) { final PsiBuilder.Marker fileMarker = builder.mark(); final ArrayList<PsiBuilder.Marker> containerMarkers = new ArrayList<>(); if (myContainerTypes != null) { for (IElementType ignored : myContainerTypes) { final PsiBuilder.Marker containerMarker = builder.mark(); containerMarkers.add(containerMarker); } } final PsiBuilder.Marker documentMarker = builder.mark(); while (builder.getTokenType() == myEolTokenType) { advanceLexer(builder); } int currentIndent = 0; boolean eolSeen = false; if (builder.getTokenType() == myIndentTokenType) { currentIndent = builder.getTokenText().length(); advanceLexer(builder); } final Stack<BlockInfo> stack = new Stack<>(); stack.push(new BlockInfo(currentIndent, builder.mark(), builder.getTokenType())); PsiBuilder.Marker startLineMarker = null; while (!builder.eof()) { final IElementType type = builder.getTokenType(); // EOL if (type == myEolTokenType) { // Handle variant with several EOLs if (startLineMarker == null) { startLineMarker = builder.mark(); } eolSeen = true; currentIndent = 0; } else { if (type == myIndentTokenType) { //noinspection ConstantConditions currentIndent = builder.getTokenText().length(); } else { if (!eolSeen && !stack.isEmpty() && currentIndent > 0 && currentIndent < stack.peek().getIndent()) { // sometimes we do not have EOL between indents eolSeen = true; } if (isCustomTagDelimiter(type)) { builder.advanceLexer(); stack.push(new BlockInfo(currentIndent, builder.mark(), type)); } if (eolSeen) { if (startLineMarker != null) { startLineMarker.rollbackTo(); startLineMarker = null; } // Close indentation blocks while (!stack.isEmpty() && currentIndent < stack.peek().getIndent()) { final BlockInfo blockInfo = stack.pop(); closeBlock(builder, blockInfo.getMarker(), blockInfo.getStartTokenType()); } if (!stack.isEmpty()) { final BlockInfo blockInfo = stack.peek(); if (currentIndent >= blockInfo.getIndent()) { if (currentIndent == blockInfo.getIndent()) { final BlockInfo info = stack.pop(); closeBlock(builder, info.getMarker(), info.getStartTokenType()); } passEOLsAndIndents(builder); stack.push(new BlockInfo(currentIndent, builder.mark(), type)); } } eolSeen = false; currentIndent = 0; } } } advanceLexer(builder); } // Close all left opened markers if (startLineMarker != null){ startLineMarker.drop(); } while (!stack.isEmpty()){ final BlockInfo blockInfo = stack.pop(); closeBlock(builder, blockInfo.getMarker(), blockInfo.getStartTokenType()); } documentMarker.done(myDocumentType); if (myContainerTypes != null) { for (int i = containerMarkers.size() - 1; i >= 0; i--) { final PsiBuilder.Marker marker = containerMarkers.get(i); marker.done(myContainerTypes.get(i)); } } fileMarker.done(root); return builder.getTreeBuilt(); }