StartMarker () { if (myProduction.isEmpty()) { LOG.error("Parser produced no markers. Text:\n" + myText); } // build tree only once to avoid threading issues in read-only PSI StartMarker rootMarker = (StartMarker)Objects.requireNonNull(myProduction.getStartMarkerAt(0)); if (rootMarker.myFirstChild != null) return rootMarker; myTokenTypeChecked = true; balanceWhiteSpaces(); rootMarker.myParent = rootMarker.myFirstChild = rootMarker.myLastChild = rootMarker.myNext = null; StartMarker curNode = rootMarker; ObjectArrayList<StartMarker> nodes = new ObjectArrayList<>(); nodes.push(rootMarker); int lastErrorIndex = -1; int maxDepth = 0; int curDepth = 0; boolean hasCollapsedChameleons = false; int[] productions = myProduction.elements(); Object[] markers = myPool.elements(); for (int i = 1, size = myProduction.size(); i < size; i++) { int id = productions[i]; ProductionMarker item = id > 0 ? (ProductionMarker)markers[id] : null; if (item instanceof StartMarker) { StartMarker marker = (StartMarker)item; marker.myParent = curNode; marker.myFirstChild = marker.myLastChild = marker.myNext = null; curNode.addChild(marker); nodes.push(curNode); curNode = marker; curDepth++; if (curDepth > maxDepth) maxDepth = curDepth; } else if (item instanceof ErrorItem) { item.myParent = curNode; int curToken = item.myLexemeIndex; if (curToken == lastErrorIndex) continue; lastErrorIndex = curToken; curNode.addChild(item); } else { if (isCollapsedChameleon(curNode)) { hasCollapsedChameleons = true; } assertMarkersBalanced(id < 0 && markers[-id] == curNode, item); curNode = nodes.pop(); curDepth--; } } if (myCurrentLexeme < myLexemeCount) { List<IElementType> missed = ContainerUtil.subArrayAsList(myLexTypes, myCurrentLexeme, myLexemeCount); LOG.error("Tokens " + missed + " were not inserted into the tree. " +(myFile != null? myFile.getLanguage()+", ":"")+"Text:\n" + myText); } if (rootMarker.getEndIndex() < myLexemeCount) { List<IElementType> missed = ContainerUtil.subArrayAsList(myLexTypes, rootMarker.getEndIndex(), myLexemeCount); LOG.error("Tokens " + missed + " are outside of root element \"" + rootMarker.myType + "\". Text:\n" + myText); } assertMarkersBalanced(curNode == rootMarker, curNode); checkTreeDepth(maxDepth, rootMarker.getTokenType() instanceof IFileElementType, hasCollapsedChameleons); clearCachedTokenType(); return rootMarker; }