PSI is an API provided by the IntelliJ Platform. It represents the abstract syntax tree (AST) of source code in a more accessible and editable way. For Kotlin, the Kotlin PSI models the syntactic structure of Kotlin code as a tree of PSI elements. Each node in the tree corresponds to a syntactic construct in the Kotlin language, such as classes, functions, variables, and expressions.
Kotlin PSI elements are the building blocks of the PSI tree. They are represented by classes that inherit from KtElement
. Some common PSI elements include:
KtClass
: Represents a Kotlin class.KtFunction
: Represents a Kotlin function.KtProperty
: Represents a Kotlin property.The PSI tree is a hierarchical structure that organizes PSI elements. The root of the tree is usually a KtFile
object, which represents a Kotlin source file. Each PSI element can have child elements, and you can traverse the tree to access different parts of the code.
Kotlin PSI can be used to analyze the structure and semantics of Kotlin code. For example, you can write a code analyzer to find all functions in a Kotlin file that have a certain number of parameters or to detect potential null pointer exceptions.
Refactoring is the process of restructuring existing code without changing its external behavior. Kotlin PSI enables developers to implement refactoring operations such as renaming classes, functions, or variables, extracting methods, and inlining code.
Kotlin PSI is the foundation for creating IDE plugins. You can use it to add new features to the IDE, such as custom code inspections, code generation templates, or navigation aids.
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiManager
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtFile
fun findAllClassesInFile(project: Project, filePath: String) {
// Get the PsiFile from the file path
val virtualFile = com.intellij.openapi.vfs.VfsUtil.findFileByIoFile(java.io.File(filePath), true)
if (virtualFile != null) {
val psiFile = PsiManager.getInstance(project).findFile(virtualFile) as? KtFile
if (psiFile != null) {
// Traverse the PSI tree to find all classes
val classes = psiFile.children.filterIsInstance<KtClass>()
for (ktClass in classes) {
println("Found class: ${ktClass.name}")
}
}
}
}
In this example, we first get the KtFile
object from a given file path. Then we traverse the children of the KtFile
to find all KtClass
elements and print their names.
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiManager
import com.intellij.psi.PsiNameIdentifierOwner
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtFile
fun renameClass(project: Project, filePath: String, oldName: String, newName: String) {
val virtualFile = com.intellij.openapi.vfs.VfsUtil.findFileByIoFile(java.io.File(filePath), true)
if (virtualFile != null) {
val psiFile = PsiManager.getInstance(project).findFile(virtualFile) as? KtFile
if (psiFile != null) {
val classes = psiFile.children.filterIsInstance<KtClass>()
for (ktClass in classes) {
if (ktClass.name == oldName) {
(ktClass as PsiNameIdentifierOwner).setName(newName)
}
}
}
}
}
This example shows how to rename a Kotlin class in a file. We first find the class with the old name and then use the setName
method to change its name.
When working with Kotlin PSI, it’s important to handle errors properly. For example, when getting a KtFile
from a file path, the file may not exist or may not be a valid Kotlin file. Always check for null values and handle exceptions gracefully.
Traversing large PSI trees can be time - consuming. Try to optimize your code by using caching mechanisms or by only traversing the parts of the tree that are relevant to your task.
The IntelliJ Platform provides many useful APIs for working with PSI. Make sure to use them correctly and follow the recommended coding patterns to ensure that your code is compatible with the IDE’s internal mechanisms.
Kotlin PSI is a powerful tool that allows developers to analyze, manipulate, and transform Kotlin source code programmatically. By understanding the core concepts, exploring typical usage scenarios, and following best practices, intermediate - to - advanced software engineers can leverage Kotlin PSI to create more efficient code analyzers, refactoring tools, and IDE plugins.