PSI Introduction
To make a sample, demonstrating the usage of PSI, I’ve created a small (but still useful) plugin which allows a user to navigate to/from test classes/methods and create them if they are not found. Plugin sources can be found at the end of this page. You can take them and do whatever you want but please don’t change the archive on this page.
PSI subsystem maintains the information about the source code in a tree which is very like AST with methods for modifying it (for details about AST please refer to any book which covers parsing & compilation techniques – e.g. “The Dragon book” http://makeashorterlink.com/?T27E31242 ). All PSI tree member classes are descendants of com.intellij.psi.PsiElement (package com.intellij.psi will be omitted in the text below) class which has the basic methods for navigating/changing PSI. Several other classes which are worth mentioning:
- PsiFile – all files are represented in PSI by descendants of this file
- PsiClass, PsiMethod – should be quite straightforward what are they representing
- PsiManager – project level component with bunch of methods for obtaining PsiElements. PsiManager can be obtained from any PSI element.
- PsiUtil – loads of useful PSI methods (almost all of them can be implemented by using the opened PSI interface)
Documents and PSI
There’s one more place in IDEA which holds information about sources: editor documents. They are not synchronized automatically with PSI. So, before accessing PSI you should call
com.intellij.codeEditor.CodeEditorManager.getInstance(myProject).commitAllToPsiFile()
which will store all changes in documents to PSI tree. DON’T FORGET to call this method or you’ll be in a trouble.
All changes to PSI are automatically mirrored in the corresponding documents, so you don’t have to synchronize in the opposite direction.
Note: For Aurora builds, the functionality of CodeEditorManager has apparently been moved to PsiDocumentManager. To accomplish the same thing:
com.intellij.psi.PsiDocumentManager pdm = PsiDocumentManager.getInstance(project);
// to commit a single document:
pdm.commitDocument(document);
// or to commit all documents:
pdm.commitAllDocuments();
-- DaveKriewall - 25 Apr 2003
Obtaining PSI elements
There are several data constants which will be useful for obtaining PSI elements from the UI:
- “psi.File” – current PSI File (in editor)
- “psi.Element”, “psi.Element.array” – current PSI element(s) in project view/commander etc.
You should also use PsiFile.findElementAt(int offset) method for locating the PSI elements, which correspond to document offset (e.g. caret offset).
Creating PSI elements
Follow these steps for creating new psi elements (e.g. methods):
- Create a simple stub through PsiElementFactory (obtained through PsiManager).
- Incorporate it into source tree
- Reformat stub
- Change its body/name/throws list etc.
Look at GoToUnitTestHandler.createTestMethod for a sample
Modifying PSI
PsiElement has a bunch of methods for changing it’s structure. All of them take a PSI element and incorporate it’s COPY into PSI tree & return the result (add, addAfter, addBefore, replace, delete etc). Please don’t forget that you should work with returned result.
Finding out the PSI structure
You’ll often find that you need to know the PSI structure of some code fragment (either for creating/analyzing it). You can use tree navigation methods & toString() for printing out the tree.
Good luck!
-- MikeAizatsky - 28 Oct 2002
|
|