IntelliJ Platform SDK DevGuide

Edit page

6. PSI Helpers and Utilities

Helper classes and utilities can be embedded in the code generated by Grammar-Kit.

6.1. Define Helper Methods for Generated PSI Elements

Custom methods in PSI classes are defined separately, and Grammar-Kit embeds them into generated code. Define a utility class with these helper methods:

package org.intellij.sdk.language.psi.impl; import com.intellij.lang.ASTNode; public class SimplePsiImplUtil { public static String getKey(SimpleProperty element) { ASTNode keyNode = element.getNode().findChildByType(SimpleTypes.KEY); if (keyNode != null) { // IMPORTANT: Convert embedded escaped spaces to simple spaces return keyNode.getText().replaceAll("\\\\ ", " "); } else { return null; } } public static String getValue(SimpleProperty element) { ASTNode valueNode = element.getNode().findChildByType(SimpleTypes.VALUE); if (valueNode != null) { return valueNode.getText(); } else { return null; } } }

The parser generates the SimpleProperty interface referenced in the code above.

6.2. Update Grammar and Regenerate the Parser

Now the utility class is added to the grammar file via the psiImplUtilClass attribute. Add methods for a particular rule to specify which one should be used for PSI classes. Compare the last line of the grammar below to the previous definition.

{ parserClass="org.intellij.sdk.language.parser.SimpleParser" extends="com.intellij.extapi.psi.ASTWrapperPsiElement" psiClassPrefix="Simple" psiImplClassSuffix="Impl" psiPackage="org.intellij.sdk.language.psi" psiImplPackage="org.intellij.sdk.language.psi.impl" elementTypeHolderClass="org.intellij.sdk.language.psi.SimpleTypes" elementTypeClass="org.intellij.sdk.language.psi.SimpleElementType" tokenTypeClass="org.intellij.sdk.language.psi.SimpleTokenType" psiImplUtilClass="org.intellij.sdk.language.psi.impl.SimplePsiImplUtil" } simpleFile ::= item_* private item_ ::= (property|COMMENT|CRLF) property ::= (KEY? SEPARATOR VALUE?) | KEY {methods=[getKey getValue]}

After making changes to the grammar, regenerate the parser and PSI classes.

6.3. Define a Utility to Search Properties

Create a utility class to search PSI elements for defined properties over the project. This utility will be used later when implementing code completion.

// Copyright 2000-2020 JetBrains s.r.o. and other contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.intellij.sdk.language; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiManager; import com.intellij.psi.search.*; import com.intellij.psi.util.PsiTreeUtil; import org.intellij.sdk.language.psi.SimpleFile; import org.intellij.sdk.language.psi.SimpleProperty; import java.util.*; public class SimpleUtil { // Searches the entire project for Simple language files with instances of the Simple property with the given key public static List<SimpleProperty> findProperties(Project project, String key) { List<SimpleProperty> result = new ArrayList<>(); Collection<VirtualFile> virtualFiles = FileTypeIndex.getFiles(SimpleFileType.INSTANCE, GlobalSearchScope.allScope(project)); for (VirtualFile virtualFile : virtualFiles) { SimpleFile simpleFile = (SimpleFile) PsiManager.getInstance(project).findFile(virtualFile); if (simpleFile != null) { SimpleProperty[] properties = PsiTreeUtil.getChildrenOfType(simpleFile, SimpleProperty.class); if (properties != null) { for (SimpleProperty property : properties) { if (key.equals(property.getKey())) { result.add(property); } } } } } return result; } public static List<SimpleProperty> findProperties(Project project) { List<SimpleProperty> result = new ArrayList<>(); Collection<VirtualFile> virtualFiles = FileTypeIndex.getFiles(SimpleFileType.INSTANCE, GlobalSearchScope.allScope(project)); for (VirtualFile virtualFile : virtualFiles) { SimpleFile simpleFile = (SimpleFile) PsiManager.getInstance(project).findFile(virtualFile); if (simpleFile != null) { SimpleProperty[] properties = PsiTreeUtil.getChildrenOfType(simpleFile, SimpleProperty.class); if (properties != null) { Collections.addAll(result, properties); } } } return result; } }
Last modified: 19 February 2020