private static TreeElement findNextLeafElementAt(ASTNode scopeNode, TreeElement last, int offset) { int offsetR = offset; if (last !=null) { offsetR -= last.getStartOffset() - scopeNode.getStartOffset() + last.getTextLength(); while (offsetR >= 0) { TreeElement next = last.getTreeNext(); if (next == null) { last = last.getTreeParent(); continue; } int length = next.getTextLength(); offsetR -= length; last = next; } scopeNode = last; offsetR += scopeNode.getTextLength(); } return (LeafElement)scopeNode.findLeafElementAt(offsetR); }
@Override public void visitLeaf(LeafElement leaf) { String leafText = leaf instanceof ForeignLeafPsiElement ? "" : leaf.getText(); catLeafs.append(leafText); final TextRange leafRange = leaf.getTextRange(); StringBuilder leafEncodedText = constructTextFromHostPSI(leafRange.getStartOffset(), leafRange.getEndOffset()); if (leaf.getElementType() == TokenType.WHITE_SPACE && prevElementTail != null) { // optimization: put all garbage into whitespace leafEncodedText.insert(0, prevElementTail); newTexts.remove(prevElement); storeUnescapedTextFor(prevElement, null); } if (!Comparing.equal(leafText, leafEncodedText)) { newTexts.put(leaf, leafEncodedText.toString()); storeUnescapedTextFor(leaf, leafText); } prevElementTail = StringUtil.startsWith(leafEncodedText, leafText) && leafEncodedText.length() != leafText.length() ? leafEncodedText.substring(leafText.length()) : null; prevElement = leaf; }
@Override public int getNodeHash(PsiElement node) { if (node == null) { return 0; } if (node instanceof PsiWhiteSpace || node instanceof PsiErrorElement) { return 0; } else if (node instanceof LeafElement) { if (isToSkipAsLiteral(node)) { return 0; } return node.getText().hashCode(); } return node.getClass().getName().hashCode(); }
public static boolean isIgnoredNode(PsiElement element) { // ex. "var i = 0" in AS: empty JSAttributeList should be skipped /*if (element.getText().length() == 0) { return true; }*/ if (element instanceof PsiWhiteSpace || element instanceof PsiErrorElement || element instanceof PsiComment) { return true; } if (!(element instanceof LeafElement)) { return false; } if (CharArrayUtil.containsOnlyWhiteSpaces(element.getText())) { return true; } EquivalenceDescriptorProvider descriptorProvider = EquivalenceDescriptorProvider.getInstance(element); if (descriptorProvider == null) { return false; } final IElementType elementType = ((LeafElement)element).getElementType(); return descriptorProvider.getIgnoredTokens().contains(elementType); }
public void _testPerformance1() throws Exception { final String text = loadFile("pallada.xml"); long time = System.currentTimeMillis(); final PsiFile file = createFile("test.xml", text); transformAllChildren(file.getNode()); System.out.println("Old parsing took " + (System.currentTimeMillis() - time) + "ms"); int index = 0; while (index++ < 10) { newParsing(text); } LeafElement firstLeaf = TreeUtil.findFirstLeaf(file.getNode()); index = 0; do { index++; } while ((firstLeaf = TreeUtil.nextLeaf(firstLeaf, null)) != null); System.out.println("For " + index + " lexems"); }
public void _testPerformance2() throws Exception { final String text = loadFile("performance2.xml"); long time = System.currentTimeMillis(); final PsiFile file = createFile("test.xml", text); transformAllChildren(file.getNode()); System.out.println("Old parsing took " + (System.currentTimeMillis() - time) + "ms"); int index = 0; while (index++ < 10) { newParsing(text); } LeafElement firstLeaf = TreeUtil.findFirstLeaf(file.getNode()); index = 0; do { index++; } while ((firstLeaf = TreeUtil.nextLeaf(firstLeaf, null)) != null); System.out.println("For " + index + " lexems"); }
/** Create a parse tree (AST) leaf node from a token. Doubles as a PSI leaf node. * Does not see whitespace tokens. Default impl makes {@link LeafPsiElement} * or {@link PsiCoreCommentImpl} depending on {@link ParserDefinition#getCommentTokens()}. */ @NotNull @Override public LeafElement createLeaf(@NotNull IElementType type, CharSequence text) { if ( type instanceof TokenIElementType && ((TokenIElementType) type).getANTLRTokenType()==SampleLanguageLexer.ID) { // found an ID node; here we do not distinguish between definitions and references // because we have no context information here. All we know is that // we have an identifier node that will be connected somewhere in a tree. // // You can only rename, find usages, etc... on leaves implementing PsiNamedElement // // TODO: try not to create one for IDs under def subtree roots like vardef, function return new IdentifierPSINode(type, text); } LeafElement leaf = super.createLeaf(type, text); return leaf; }
private static TreeElement findNextLeafElementAt(ASTNode scopeNode, TreeElement last, int offset) { int offsetR = offset; if (last != null) { offsetR -= last.getStartOffset() - scopeNode.getStartOffset() + last.getTextLength(); while (offsetR >= 0) { TreeElement next = last.getTreeNext(); if (next == null) { last = last.getTreeParent(); continue; } int length = next.getTextLength(); offsetR -= length; last = next; } scopeNode = last; offsetR += scopeNode.getTextLength(); } return (LeafElement)scopeNode.findLeafElementAt(offsetR); }
@Nonnull @Override public LeafElement createLeaf(@Nonnull IElementType type, @Nonnull LanguageVersion languageVersion, @Nonnull CharSequence text) { final ParserDefinition parserDefinition = LanguageParserDefinitions.INSTANCE.forLanguage(type.getLanguage()); if(parserDefinition != null) { if(parserDefinition.getCommentTokens(languageVersion).contains(type)) { return new PsiCoreCommentImpl(type, text); } } // this is special case, then type is WHITE_SPACE, but no parser definition if(type == TokenType.WHITE_SPACE) { return new PsiWhiteSpaceImpl(text); } if (type instanceof ILeafElementType) { return (LeafElement)((ILeafElementType)type).createLeafNode(text); } return new LeafPsiElement(type, text); }
public static void expandTag(@NotNull XmlTag tag) { XmlTag newTag = XmlElementFactory.getInstance(tag.getProject()).createTagFromText('<' + tag.getName() + "></" + tag.getName() + '>'); ASTNode node = tag.getNode(); if(!(node instanceof CompositeElement)) { return; } CompositeElement compositeElement = (CompositeElement) node; final LeafElement emptyTagEnd = (LeafElement) XmlChildRole.EMPTY_TAG_END_FINDER.findChild(compositeElement); if(emptyTagEnd == null) { return; } compositeElement.removeChild(emptyTagEnd); PsiElement[] children = newTag.getChildren(); compositeElement.addChildren(children[2].getNode(), null, null); }
@Override @Nullable public XmlAttribute getAttribute(String qname) { if(qname == null) { return null; } final XmlAttribute[] attributes = getAttributes(); final boolean caseSensitive = isCaseSensitive(); for(final XmlAttribute attribute : attributes) { final ASTNode child = XmlChildRole.ATTRIBUTE_NAME_FINDER.findChild(attribute.getNode()); if(child instanceof LeafElement) { final LeafElement attrNameElement = (LeafElement) child; if((caseSensitive && Comparing.equal(attrNameElement.getChars(), qname) || !caseSensitive && Comparing.equal(attrNameElement.getChars(), qname, false))) { return attribute; } } } return null; }
private ASTNode expandTag() throws IncorrectOperationException { ASTNode endTagStart = XmlChildRole.CLOSING_TAG_START_FINDER.findChild(this); if(endTagStart == null) { final XmlTagImpl tagFromText = (XmlTagImpl) XmlElementFactory.getInstance(getProject()).createTagFromText("<" + getName() + "></" + getName() + ">"); final ASTNode startTagStart = XmlChildRole.START_TAG_END_FINDER.findChild(tagFromText); endTagStart = XmlChildRole.CLOSING_TAG_START_FINDER.findChild(tagFromText); final LeafElement emptyTagEnd = (LeafElement) XmlChildRole.EMPTY_TAG_END_FINDER.findChild(this); if(emptyTagEnd != null) { removeChild(emptyTagEnd); } addChildren(startTagStart, null, null); } return endTagStart; }
@Override public PsiImportStaticStatement bindToTargetClass(final PsiClass aClass) throws IncorrectOperationException { final String qualifiedName = aClass.getQualifiedName(); if (qualifiedName == null) throw new IncorrectOperationException(); final PsiJavaParserFacade parserFacade = JavaPsiFacade.getInstance(getProject()).getParserFacade(); final CompositeElement newRef = (CompositeElement)parserFacade.createReferenceFromText(qualifiedName, null).getNode(); if (getQualifier() != null) { replaceChildInternal(findChildByRole(ChildRole.QUALIFIER), newRef); return (PsiImportStaticStatement)getParent(); } else { final LeafElement dot = Factory.createSingleLeafElement(JavaTokenType.DOT, ".", 0, 1, SharedImplUtil.findCharTableByTree(newRef), getManager()); newRef.rawInsertAfterMe(dot); final CompositeElement errorElement = Factory.createErrorElement(JavaErrorMessages.message("import.statement.identifier.or.asterisk.expected.")); dot.rawInsertAfterMe(errorElement); final CompositeElement parentComposite = (CompositeElement)SourceTreeToPsiMap.psiElementToTree(getParent()); parentComposite.addInternal(newRef, errorElement, this, Boolean.TRUE); parentComposite.deleteChildInternal(this); return (PsiImportStaticStatement)SourceTreeToPsiMap.treeElementToPsi(parentComposite); } }
private static ASTNode getLastChildOf(ASTNode element) { if (element == null) { return null; } if (element instanceof LeafElement) { return element; } else { final ASTNode lastChild = element.getLastChildNode(); if (lastChild == null) { return element; } else { return getLastChildOf(lastChild); } } }
@NotNull private static int[] findAllTypedVarOffsets(final PsiFile file, final Pattern[] substitutionPatterns) { final TIntHashSet result = new TIntHashSet(); file.accept(new PsiRecursiveElementWalkingVisitor() { @Override public void visitElement(PsiElement element) { super.visitElement(element); if (element instanceof LeafElement) { final String text = element.getText(); for (Pattern pattern : substitutionPatterns) { final Matcher matcher = pattern.matcher(text); while (matcher.find()) { result.add(element.getTextRange().getStartOffset() + matcher.end()); } } } } }); final int[] resultArray = result.toArray(); Arrays.sort(resultArray); return resultArray; }
@NotNull @Override public CompiledPattern createCompiledPattern() { return new CompiledPattern() { @Override protected SubstitutionHandler doCreateSubstitutionHandler(String name, boolean target, int minOccurs, int maxOccurs, boolean greedy) { return new MySubstitutionHandler(name, target, minOccurs, maxOccurs, greedy); } @Override public String[] getTypedVarPrefixes() { return getVarPrefixes(); } @Override public boolean isTypedVar(String str) { for (String prefix : getVarPrefixes()) { if (str.startsWith(prefix)) { return true; } } return false; } @NotNull @Override public String getTypedVarString(PsiElement element) { final PsiElement initialElement = element; PsiElement child = SkippingHandler.getOnlyNonWhitespaceChild(element); while (child != element && child != null && !(child instanceof LeafElement)) { element = child; child = SkippingHandler.getOnlyNonWhitespaceChild(element); } return child instanceof LeafElement ? element.getText() : initialElement.getText(); } }; }
private static boolean canBePatternVariable(PsiElement element) { // can be leaf element! (ex. var a = 1 <-> var $a$ = 1) if (element instanceof LeafElement) { return true; } while (!(element instanceof LeafElement) && element != null) { element = SkippingHandler.getOnlyNonWhitespaceChild(element); } return element != null; }
private void doVisitElement(PsiElement element) { CompiledPattern pattern = myGlobalVisitor.getContext().getPattern(); if (myGlobalVisitor.getCodeBlockLevel() == 0) { initTopLevelElement(element); return; } if (canBePatternVariable(element) && pattern.isRealTypedVar(element)) { myGlobalVisitor.handle(element); final MatchingHandler handler = pattern.getHandler(element); handler.setFilter(new NodeFilter() { public boolean accepts(PsiElement other) { return canBePatternVariableValue(other); } }); super.visitElement(element); return; } super.visitElement(element); if (myGlobalVisitor.getContext().getSearchHelper().doOptimizing() && element instanceof LeafElement) { ParserDefinition parserDefinition = LanguageParserDefinitions.INSTANCE.forLanguage(element.getLanguage()); if (parserDefinition != null) { String text = element.getText(); // todo: support variables inside comments boolean flag = true; if (StringUtil.isJavaIdentifier(text) && flag) { myGlobalVisitor.processTokenizedName(text, true, GlobalCompilingVisitor.OccurenceKind.CODE); } } } }
private boolean checkLeaf(final ASTNode treeNext, final CharSequence charTabIndex) { if(!(treeNext instanceof LeafElement)) return false; final ChangeInfo right = myChanges.get(treeNext); if(right != null && right.getChangeType() == ChangeInfo.ADD){ if(charTabIndex == treeNext.getChars()){ removeChangeInternal(treeNext); return true; } } return false; }
@NotNull public static LeafElement leaf(@NotNull final IElementType type, @NotNull CharSequence text) { if (type == TokenType.WHITE_SPACE) { return new PsiWhiteSpaceImpl(text); } if (type instanceof ILeafElementType) { return (LeafElement)((ILeafElementType)type).createLeafNode(text); } final LeafElement customLeaf = factory(type).createLeaf(type, text); return customLeaf != null ? customLeaf : DefaultFactoryHolder.DEFAULT.createLeaf(type, text); }
@Override @NotNull public PsiElement createWhiteSpaceFromText(@NotNull @NonNls String text) throws IncorrectOperationException { final FileElement holderElement = DummyHolderFactory.createHolder(myManager, null).getTreeElement(); final LeafElement newElement = ASTFactory.leaf(TokenType.WHITE_SPACE, holderElement.getCharTable().intern(text)); holderElement.rawAddChildren(newElement); GeneratedMarkerVisitor.markGenerated(newElement.getPsi()); return newElement.getPsi(); }
public static void replaceChild(ASTNode parent, @NotNull ASTNode oldChild, @NotNull ASTNode newChild) { saveWhitespacesInfo(oldChild); saveWhitespacesInfo(newChild); checkForOuters(oldChild); checkForOuters(newChild); LeafElement oldFirst = TreeUtil.findFirstLeaf(oldChild); parent.replaceChild(oldChild, newChild); final LeafElement firstLeaf = TreeUtil.findFirstLeaf(newChild); final ASTNode prevToken = TreeUtil.prevLeaf(newChild); if (firstLeaf != null) { final ASTNode nextLeaf = TreeUtil.nextLeaf(newChild); makePlaceHolderBetweenTokens(prevToken, firstLeaf, isFormattingRequired(prevToken, newChild), false); if (nextLeaf != null && !CharArrayUtil.containLineBreaks(nextLeaf.getText())) { makePlaceHolderBetweenTokens(TreeUtil.prevLeaf(nextLeaf), nextLeaf, false, false); } } else { if (oldFirst != null && prevToken == null) { ASTNode whitespaceNode = newChild.getTreeNext(); if (whitespaceNode != null && whitespaceNode.getElementType() == TokenType.WHITE_SPACE) { // Replacing non-empty prefix to empty shall remove whitespace parent.removeChild(whitespaceNode); } } makePlaceHolderBetweenTokens(prevToken, TreeUtil.nextLeaf(newChild), isFormattingRequired(prevToken, newChild), false); } }
@Nullable private static ASTNode findFirstLeaf(ASTNode first, ASTNode last) { do { final LeafElement leaf = TreeUtil.findFirstLeaf(first); if (leaf != null) return leaf; first = first.getTreeNext(); if (first == null) return null; } while (first != last); return null; }
@Override public LeafElement split(LeafElement leaf, int offset, final CharTable table) { final CharSequence chars = leaf.getChars(); final LeafElement leftPart = ASTFactory.leaf(leaf.getElementType(), table.intern(chars, 0, offset)); final LeafElement rightPart = ASTFactory.leaf(leaf.getElementType(), table.intern(chars, offset, chars.length())); leaf.rawInsertAfterMe(leftPart); leftPart.rawInsertAfterMe(rightPart); leaf.rawRemove(); return leftPart; }
private boolean isLiteral(PsiElement node) { if (node instanceof LeafElement) { final IElementType elementType = ((LeafElement)node).getElementType(); if (myDuplicatesProfile.getLiterals().contains(elementType)) { return true; } } return false; }
public static boolean shouldSkip(PsiElement element, PsiElement elementToMatchWith) { if (element == null || elementToMatchWith == null) { return false; } if (element.getClass() == elementToMatchWith.getClass()) { return false; } if (element.getFirstChild() == null && element.getTextLength() == 0 && !(element instanceof LeafElement)) { return true; } return false; }
public static void expandTag(@NotNull XmlTag tag) { XmlTag newTag = XmlElementFactory.getInstance(tag.getProject()).createTagFromText('<' + tag.getName() + "></" + tag.getName() + '>'); ASTNode node = tag.getNode(); if (!(node instanceof CompositeElement)) return; CompositeElement compositeElement = (CompositeElement)node; final LeafElement emptyTagEnd = (LeafElement)XmlChildRole.EMPTY_TAG_END_FINDER.findChild(compositeElement); if (emptyTagEnd == null) return; compositeElement.removeChild(emptyTagEnd); PsiElement[] children = newTag.getChildren(); compositeElement.addChildren(children[2].getNode(), null, null); }
public static <T extends PsiElement> T handleContentChange(T element, TextRange range, String newContent, final IElementType tokenType) { CheckUtil.checkWritable(element); final CompositeElement attrNode = (CompositeElement)element.getNode(); final ASTNode valueNode = attrNode.findLeafElementAt(range.getStartOffset()); LOG.assertTrue(valueNode != null, "Leaf not found in " + attrNode + " at offset " + range.getStartOffset() + " in element " + element); final PsiElement elementToReplace = valueNode.getPsi(); String text; try { text = elementToReplace.getText(); final int offsetInParent = elementToReplace.getStartOffsetInParent(); String textBeforeRange = text.substring(0, range.getStartOffset() - offsetInParent); String textAfterRange = text.substring(range.getEndOffset()- offsetInParent, text.length()); newContent = element.getText().startsWith("'") || element.getText().endsWith("'") ? newContent.replace("'", "'") : newContent.replace("\"", """); text = textBeforeRange + newContent + textAfterRange; } catch(StringIndexOutOfBoundsException e) { LOG.error("Range: " + range + " in text: '" + element.getText() + "'", e); throw e; } final CharTable charTableByTree = SharedImplUtil.findCharTableByTree(attrNode); final LeafElement newValueElement = Factory.createSingleLeafElement(tokenType, text, charTableByTree, element.getManager()); attrNode.replaceChildInternal(valueNode, newValueElement); return element; }
@Override public XmlToken handleContentChange(@NotNull XmlToken xmlToken, @NotNull TextRange range, String newContent) throws IncorrectOperationException { String oldText = xmlToken.getText(); String newText = oldText.substring(0, range.getStartOffset()) + newContent + oldText.substring(range.getEndOffset()); IElementType tokenType = xmlToken.getTokenType(); FileElement holder = DummyHolderFactory.createHolder(xmlToken.getManager(), null).getTreeElement(); LeafElement leaf = ASTFactory.leaf(tokenType, holder.getCharTable().intern(newText)); holder.rawAddChildren(leaf); return (XmlToken)xmlToken.replace(leaf.getPsi()); }
private static void addWhitespaceToTagBody(final ASTNode treePrev, final LeafElement whiteSpaceElement) { final CharTable charTable = SharedImplUtil.findCharTableByTree(treePrev); final ASTNode treeParent = treePrev.getTreeParent(); final boolean before; final XmlText xmlText; if(treePrev.getElementType() == XmlElementType.XML_TEXT) { xmlText = (XmlText)treePrev.getPsi(); before = true; } else if(treePrev.getTreePrev().getElementType() == XmlElementType.XML_TEXT){ xmlText = (XmlText)treePrev.getTreePrev().getPsi(); before = false; } else{ xmlText = (XmlText)Factory.createCompositeElement(XmlElementType.XML_TEXT, charTable, treeParent.getPsi().getManager()); CodeEditUtil.setNodeGenerated(xmlText.getNode(), true); treeParent.addChild(xmlText.getNode(), treePrev); before = true; } final ASTNode node = xmlText.getNode(); assert node != null; final TreeElement anchorInText = (TreeElement) (before ? node.getFirstChildNode() : node.getLastChildNode()); if (anchorInText == null) node.addChild(whiteSpaceElement); else if (anchorInText.getElementType() != XmlTokenType.XML_WHITE_SPACE) node.addChild(whiteSpaceElement, before ? anchorInText : null); else { final String text = before ? whiteSpaceElement.getText() + anchorInText.getText() : anchorInText.getText() + whiteSpaceElement.getText(); node.replaceChild(anchorInText, ASTFactory.whitespace(text)); } }
@Override public boolean addWhitespace(@NotNull final ASTNode treePrev, @NotNull final LeafElement whiteSpaceElement) { if (isInsideTagBody(treePrev)) { addWhitespaceToTagBody(treePrev, whiteSpaceElement); return true; } return false; }
private boolean containsWhiteSpacesOnly(final ASTNode node) { if (node == null) return false; if (node.getElementType() == TokenType.WHITE_SPACE) return true; if (node instanceof LeafElement) return false; ASTNode child = node.getFirstChildNode(); while (child != null) { if (!containsWhiteSpacesOnly(child)) return false; child = child.getTreeNext(); } return true; }
@Nullable protected static ASTNode findChildAfter(@NotNull final ASTNode child, final int endOffset) { TreeElement fileNode = TreeUtil.getFileElement((TreeElement)child); final LeafElement leaf = fileNode.findLeafElementAt(endOffset); if (leaf != null && leaf.getStartOffset() == endOffset && endOffset > 0) { return fileNode.findLeafElementAt(endOffset - 1); } return leaf; }
@Override public void visitReferenceElement(PsiJavaCodeReferenceElement reference) { // Ensure that e.g. View.OnClickListener and OnClickListener match if (reference != null && candidate instanceof PsiJavaCodeReferenceElement) { // Presuming that reference resolution is expensive, only do it when simple names match (e.g. both end in 'OnClickListener') PsiJavaCodeReferenceElement other = (PsiJavaCodeReferenceElement)candidate; PsiElement simpleName1 = reference.getLastChild().getPrevSibling(); PsiElement simpleName2 = candidate.getLastChild().getPrevSibling(); if (simpleName1 instanceof LeafElement && simpleName2 instanceof LeafElement) { LeafElement leaf1 = (LeafElement)simpleName1; LeafElement leaf2 = (LeafElement)simpleName2; // Use getChars() here as, unlike getText(), this doesn't involve creating a new String. if (leaf1.getChars().equals(leaf2.getChars())) { PsiElement r1 = reference.resolve(); if (r1 == null) { LOG.warn("Pattern contains unresolvable (unqualified?) class name: " + reference.getText()); } PsiElement r2 = other.resolve(); // a.foo() and b.foo() will resolve to the same method, make sure we only compare classes if (r1 instanceof PsiClass && r2 instanceof PsiClass) { PsiClass c1 = (PsiClass)r1; PsiClass c2 = (PsiClass)r2; // When one psi class is compiled but the other is from source, the instances are not '=='. Compare qualified names. if (Objects.equal(c1.getQualifiedName(), c2.getQualifiedName())){ return; } } } } } super.visitReferenceElement(reference); }
@Override public PsiElement setName(@NotNull String name) throws IncorrectOperationException { for (PsiElement declaration : myDeclarations) { if (declaration instanceof PsiNamedElement) { if (declaration instanceof PsiMethod) { name = GroovyPropertyUtils.getGetterNameNonBoolean(name); } ((PsiNamedElement)declaration).setName(name); } else if (declaration instanceof GrArgumentLabel) { ((GrArgumentLabel)declaration).setName(name); } else if (declaration instanceof XmlAttributeValue) { PsiElement leftQuote = declaration.getFirstChild(); if (!(leftQuote instanceof XmlToken)) continue; PsiElement textToken = leftQuote.getNextSibling(); if (!(textToken instanceof XmlToken)) continue; PsiElement rightQuote = textToken.getNextSibling(); if (!(rightQuote instanceof XmlToken) || rightQuote.getNextSibling() != null) continue; ((LeafElement)textToken).replaceWithText(name); } else if (declaration instanceof GrReferenceExpression) { ((GrReferenceExpression)declaration).handleElementRenameSimple(name); } } return getNameIdentifier().replace(new GrLightIdentifier(myManager, name)); }
private void nodeToString(PsiElement psi, StringBuilder builder) { if (psi.getNode() instanceof LeafElement) { return; } PsiElement[] children = Arrays.stream(psi.getChildren()) .filter(t -> t instanceof ProjectViewPsiElement) .toArray(PsiElement[]::new); if (psi instanceof ProjectViewPsiElement) { builder.append(psi.getNode().getElementType()); appendChildren(children, builder, true); } else { appendChildren(children, builder, false); } }