ASTFactory, LeafPsiElement, ASTWrapperPsiElement and CompositElement
In the Custom Language Documentation, it is suggested that plugins should use ASTWrapperPsiElement as base for their PsiElements and I followed this rule. What always bothered me a bit was that my PsiTree contained one additional leaf element that just wrapped the underlying token. For the simple example of "f[a+b]" my tree, therefore, looked like this

So while my "Symbol" PsiElement should have been the leaf element, it was always a wrapper for an additional PsiElement that was created by the default ASTFactory. This led to situations where things like "findElementAtCaret" returned the PsiElement(IDENTIFIER) and I had to compare IElementTypes to find out if it is a variable. Tonight, I finally took the time to tackle this. I implemented an ASTFactory and changed my "atomic" PsiElements to LeafPsiElements because I didn't understand why leaves shouldn't be used as normal PsiElements. Now, my PsiTree looks really nice and clean:

It didn't take long to realise why this was a bad idea: When I now call getChildren on my FunctionCall element, I only get PsiElements that are composite because this is how it is implemented in the underlying method of ASTDelegatePsiElement
if (psiChild.getNode() instanceof CompositeElement) {
result.add(psiChild);
}
Therefore, do I understand this correct that
- I can use my ASTFactory to create customized leaf elements that I can test with instanceof instead of comparing IElementTypes?
- I should not make my Symbol PsiElements the leaves of the tree and rather keep them as parent of the underlying token-PsiElements?
Please sign in to leave a comment.
Yes on both points. Normally, lexer tokens are transformed leaf elements, and they don't usually play a significant role in PSI interface. They usually serve as an implementation detail for the actual PSI, e.g. that's where symbols take their names from. Clients shouldn't care much about those elements, and comparing by element types is mostly done inside the (composite) PSI elements. The clients should operate in terms of higher-level abstractions, like getName/setName.
Thank you Peter for the clarification.