- Define an interface for the stub, derived from the StubElement interface (example)
- Provide an implementation for the interface (example)
- Make sure that the interface for the PSI element extends StubBasedPsiElement parameterized by the type of the stub interface (example)
- Make sure that the implementation class for the PSI element extends StubBasedPsiElementBase parameterized by the type of the stub interface (example). Provide both a constructor that accepts an ASTNode and a constructor which accepts a stub.
- Create a class which implements IStubElementType and is parameterized with the stub interface and the actual PSI element interface (example). Implement the createPsi() and createStub() methods for creating PSI from a stub and vice versa. Implement the serialize() and deserialize() methods for storing the data in a binary stream.
- Use the class implementing IStubElementType as the element type constant when parsing (example)
- Make sure that all methods in the PSI element interface access the stub data rather than the PSI tree when appropriate (example: Property.getKey() implementation|https://github.com/JetBrains/intellij-community/blob/master/plugins/properties/src/com/intellij/lang/properties/psi/impl/PropertyImpl.java#L97)
The following steps need to be performed only once for each language that supports stubs:
- Change the file element type for your language (the element type that you return from ParserDefinition.getFileNodeType()) to a class that extends IStubFileElementType.
- In your plugin.xml, define the <stubElementTypeHolder> extension and specify the interface which contains the IElementType constants used by your language's parser (example).
If you need to change the stored binary format for the stubs (for example, if you want to store some additional data or some new elements), make sure that you advance the stub version returned from IStubFileElementType.getStubVersion() for your language. This will cause the stubs and stub indices to be rebuilt, and will avoid mismatches between the stored data format and the code trying to load it.
By default, if a PSI element extends StubBasedPsiElement, all elements of that type will be stored in the stub tree. If you need more precise control over which elements are stored, override IStubElementType.shouldCreateStub() and return false for elements which should not be included in the stub tree. Note that the exclusion is not recursive: if some elements of the element for which you returned false are also stub-based PSI elements, they will be included in the stub tree.
It's essential to make sure that all information stored in the stub tree depends only on the contents of the file for which stubs are being built, and does not depend on any external files. Otherwise the stub tree will not be rebuilt when an external dependency changes, and you will have stale and incorrect data in the stub tree.